Skip to content

Commit b41955f

Browse files
neumieclaude
andauthored
feat: add CreateWorktree API action for programmatic worktree creation (#92)
Expose a new CreateWorktree action through the API that computes target paths, creates the worktree project, spawns terminals, and returns the new project_id, terminal_id, and path. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5fcae04 commit b41955f

File tree

4 files changed

+56
-0
lines changed

4 files changed

+56
-0
lines changed

crates/okena-core/src/api.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,12 @@ pub enum ActionRequest {
259259
ReloadServices {
260260
project_id: String,
261261
},
262+
CreateWorktree {
263+
project_id: String,
264+
branch: String,
265+
#[serde(default)]
266+
create_branch: bool,
267+
},
262268
}
263269

264270
/// POST /v1/pair request

crates/okena-git/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ pub use repository::{
1010
get_available_branches_for_worktree,
1111
get_repo_root,
1212
resolve_git_root_and_subdir,
13+
compute_target_paths,
14+
project_path_in_worktree,
1315
has_uncommitted_changes,
1416
get_current_branch,
1517
get_default_branch,

src/action_dispatch.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,5 +564,14 @@ fn strip_remote_ids(action: ActionRequest, connection_id: &str) -> ActionRequest
564564
ActionRequest::ReloadServices { project_id } => ActionRequest::ReloadServices {
565565
project_id: s(&project_id),
566566
},
567+
ActionRequest::CreateWorktree {
568+
project_id,
569+
branch,
570+
create_branch,
571+
} => ActionRequest::CreateWorktree {
572+
project_id: s(&project_id),
573+
branch,
574+
create_branch,
575+
},
567576
}
568577
}

src/workspace/actions/execute.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,35 @@ pub fn execute_action(
368368
| ActionRequest::ReloadServices { .. } => {
369369
ActionResult::Err("service actions must be handled via ServiceManager".to_string())
370370
}
371+
ActionRequest::CreateWorktree { project_id, branch, create_branch } => {
372+
let project = match ws.project(&project_id) {
373+
Some(p) => p,
374+
None => return ActionResult::Err(format!("project not found: {}", project_id)),
375+
};
376+
let project_path = std::path::PathBuf::from(&project.path);
377+
let (git_root, subdir) = okena_git::resolve_git_root_and_subdir(&project_path);
378+
let path_template = settings(cx).worktree.path_template.clone();
379+
let (worktree_path, wt_project_path) = okena_git::compute_target_paths(&git_root, &subdir, &path_template, &branch);
380+
let global_hooks = settings(cx).hooks.clone();
381+
382+
match ws.create_worktree_project(&project_id, &branch, &git_root, &worktree_path, &wt_project_path, create_branch, &global_hooks, cx) {
383+
Ok(new_project_id) => {
384+
let result = spawn_uninitialized_terminals(ws, &new_project_id, backend, terminals, cx);
385+
let terminal_id = ws.project(&new_project_id)
386+
.and_then(|p| p.layout.as_ref())
387+
.and_then(|l| find_first_terminal_id(l));
388+
match result {
389+
ActionResult::Ok(_) => ActionResult::Ok(Some(serde_json::json!({
390+
"project_id": new_project_id,
391+
"terminal_id": terminal_id,
392+
"path": wt_project_path,
393+
}))),
394+
err => err,
395+
}
396+
}
397+
Err(e) => ActionResult::Err(e),
398+
}
399+
}
371400
}
372401
}
373402

@@ -508,6 +537,16 @@ pub fn spawn_uninitialized_terminals(
508537
ActionResult::Ok(None)
509538
}
510539

540+
/// Find the first terminal_id in a layout tree (depth-first).
541+
fn find_first_terminal_id(node: &LayoutNode) -> Option<String> {
542+
match node {
543+
LayoutNode::Terminal { terminal_id, .. } => terminal_id.clone(),
544+
LayoutNode::Split { children, .. } | LayoutNode::Tabs { children, .. } => {
545+
children.iter().find_map(find_first_terminal_id)
546+
}
547+
}
548+
}
549+
511550
/// Find the layout path for a terminal within a project.
512551
pub fn find_terminal_path(
513552
ws: &Workspace,

0 commit comments

Comments
 (0)