Skip to content

Commit 8a1e19c

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 c4fa28a commit 8a1e19c

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
@@ -147,6 +147,9 @@ impl CloseWorktreeDialog {
147147
.map(|p| p.hooks.clone())
148148
.unwrap_or_default();
149149
let global_hooks = self.hooks_config.clone();
150+
let folder = ws.folder_for_project_or_parent(&project_id);
151+
let folder_id = folder.map(|f| f.id.clone());
152+
let folder_name = folder.map(|f| f.name.clone());
150153
let monitor = hooks::try_monitor(cx);
151154
let runner = hooks::try_runner(cx);
152155

@@ -223,6 +226,8 @@ impl CloseWorktreeDialog {
223226
let branch = branch.clone();
224227
let default_branch = default_branch.clone();
225228
let main_repo_path = main_repo_path.clone();
229+
let folder_id = folder_id.clone();
230+
let folder_name = folder_name.clone();
226231
let monitor = monitor.clone();
227232
move || {
228233
// Sync hooks run headlessly (no PTY) — they block the flow
@@ -236,6 +241,8 @@ impl CloseWorktreeDialog {
236241
&branch,
237242
&default_branch,
238243
&main_repo_path,
244+
folder_id.as_deref(),
245+
folder_name.as_deref(),
239246
monitor.as_ref(),
240247
None,
241248
)
@@ -285,6 +292,8 @@ impl CloseWorktreeDialog {
285292
&default_branch,
286293
&main_repo_path,
287294
&e,
295+
folder_id.as_deref(),
296+
folder_name.as_deref(),
288297
monitor.as_ref(),
289298
runner.as_ref(),
290299
);
@@ -351,6 +360,8 @@ impl CloseWorktreeDialog {
351360
&branch,
352361
&default_branch,
353362
&main_repo_path,
363+
folder_id.as_deref(),
364+
folder_name.as_deref(),
354365
monitor.as_ref(),
355366
runner.as_ref(),
356367
);
@@ -429,6 +440,8 @@ impl CloseWorktreeDialog {
429440
&project_path,
430441
&branch,
431442
&main_repo_path,
443+
folder_id.as_deref(),
444+
folder_name.as_deref(),
432445
monitor.as_ref(),
433446
runner.as_ref(),
434447
);
@@ -465,6 +478,8 @@ impl CloseWorktreeDialog {
465478
let project_path = project_path.clone();
466479
let branch = branch.clone();
467480
let main_repo_path = main_repo_path.clone();
481+
let folder_id = folder_id.clone();
482+
let folder_name = folder_name.clone();
468483
let monitor = monitor.clone();
469484
move || {
470485
hooks::fire_before_worktree_remove(
@@ -475,6 +490,8 @@ impl CloseWorktreeDialog {
475490
&project_path,
476491
&branch,
477492
&main_repo_path,
493+
folder_id.as_deref(),
494+
folder_name.as_deref(),
478495
monitor.as_ref(),
479496
None,
480497
)
@@ -504,6 +521,8 @@ impl CloseWorktreeDialog {
504521
&project_name,
505522
&project_path,
506523
&branch,
524+
folder_id.as_deref(),
525+
folder_name.as_deref(),
507526
monitor.as_ref(),
508527
runner.as_ref(),
509528
);
@@ -539,6 +558,8 @@ impl CloseWorktreeDialog {
539558
&project_path,
540559
&branch,
541560
&main_repo_path,
561+
folder_id.as_deref(),
562+
folder_name.as_deref(),
542563
monitor.as_ref(),
543564
runner.as_ref(),
544565
);

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
}
@@ -172,6 +175,9 @@ impl Workspace {
172175
/// Delete a project
173176
pub fn delete_project(&mut self, project_id: &str, global_hooks: &HooksConfig, cx: &mut Context<Self>) {
174177
// Capture project info before removal for the hook
178+
let folder = self.folder_for_project_or_parent(project_id);
179+
let hook_folder_id = folder.map(|f| f.id.clone());
180+
let hook_folder_name = folder.map(|f| f.name.clone());
175181
let hook_info = self.project(project_id).map(|p| {
176182
(p.hooks.clone(), p.id.clone(), p.name.clone(), p.path.clone())
177183
});
@@ -217,7 +223,7 @@ impl Workspace {
217223
self.notify_data(cx);
218224

219225
if let Some((project_hooks, id, name, path)) = hook_info {
220-
hooks::fire_on_project_close(&project_hooks, &id, &name, &path, global_hooks, cx);
226+
hooks::fire_on_project_close(&project_hooks, &id, &name, &path, hook_folder_id.as_deref(), hook_folder_name.as_deref(), global_hooks, cx);
221227
}
222228
}
223229

@@ -433,12 +439,17 @@ impl Workspace {
433439
self.notify_data(cx);
434440

435441
if fire_hooks {
442+
let folder = self.folder_for_project_or_parent(&id);
443+
let folder_id = folder.map(|f| f.id.as_str());
444+
let folder_name = folder.map(|f| f.name.as_str());
436445
let hook_results = hooks::fire_on_worktree_create(
437446
&new_project_hooks,
438447
&id,
439448
&new_project_name,
440449
project_path,
441450
branch,
451+
folder_id,
452+
folder_name,
442453
global_hooks,
443454
cx,
444455
);
@@ -471,12 +482,17 @@ impl Workspace {
471482
}
472483
}
473484

485+
let folder = self.folder_for_project_or_parent(project_id);
486+
let folder_id = folder.map(|f| f.id.as_str());
487+
let folder_name = folder.map(|f| f.name.as_str());
474488
let hook_results = hooks::fire_on_worktree_create(
475489
&hooks_config,
476490
project_id,
477491
&name,
478492
&path,
479493
&branch,
494+
folder_id,
495+
folder_name,
480496
global_hooks,
481497
cx,
482498
);
@@ -602,6 +618,9 @@ impl Workspace {
602618
}
603619

604620
// Capture info before removal for the hook
621+
let folder = self.folder_for_project_or_parent(project_id);
622+
let hook_folder_id = folder.map(|f| f.id.clone());
623+
let hook_folder_name = folder.map(|f| f.name.clone());
605624
let project_hooks = project.hooks.clone();
606625
let project_name = project.name.clone();
607626
let project_path = project.path.clone();
@@ -619,7 +638,7 @@ impl Workspace {
619638
self.delete_project(project_id, global_hooks, cx);
620639

621640
// Fire worktree-specific hook (runs headlessly since project is deleted)
622-
hooks::fire_on_worktree_close(&project_hooks, project_id, &project_name, &project_path, &branch, global_hooks, cx);
641+
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);
623642

624643
Ok(())
625644
}

0 commit comments

Comments
 (0)