feat(ecc2): implement session create/destroy lifecycle (#764)

- Process spawning via tokio::process::Command
- Session state transitions with guards (Pending->Running->Completed/Failed/Stopped)
- Stop with process kill and optional worktree cleanup
- Latest alias resolver in get_status
- SQLite store migrations for state tracking
This commit is contained in:
Affaan Mustafa
2026-03-23 03:46:17 -07:00
parent 2166d80d58
commit 2787b8e92f
6 changed files with 688 additions and 54 deletions

View File

@@ -1,5 +1,5 @@
use anyhow::{Context, Result};
use std::path::PathBuf;
use std::path::Path;
use std::process::Command;
use crate::config::Config;
@@ -7,16 +7,27 @@ use crate::session::WorktreeInfo;
/// Create a new git worktree for an agent session.
pub fn create_for_session(session_id: &str, cfg: &Config) -> Result<WorktreeInfo> {
let repo_root = std::env::current_dir().context("Failed to resolve repository root")?;
create_for_session_in_repo(session_id, cfg, &repo_root)
}
pub(crate) fn create_for_session_in_repo(
session_id: &str,
cfg: &Config,
repo_root: &Path,
) -> Result<WorktreeInfo> {
let branch = format!("ecc/{session_id}");
let path = cfg.worktree_root.join(session_id);
// Get current branch as base
let base = get_current_branch()?;
let base = get_current_branch(repo_root)?;
std::fs::create_dir_all(&cfg.worktree_root)
.context("Failed to create worktree root directory")?;
let output = Command::new("git")
.arg("-C")
.arg(repo_root)
.args(["worktree", "add", "-b", &branch])
.arg(&path)
.arg("HEAD")
@@ -28,7 +39,11 @@ pub fn create_for_session(session_id: &str, cfg: &Config) -> Result<WorktreeInfo
anyhow::bail!("git worktree add failed: {stderr}");
}
tracing::info!("Created worktree at {} on branch {}", path.display(), branch);
tracing::info!(
"Created worktree at {} on branch {}",
path.display(),
branch
);
Ok(WorktreeInfo {
path,
@@ -38,8 +53,10 @@ pub fn create_for_session(session_id: &str, cfg: &Config) -> Result<WorktreeInfo
}
/// Remove a worktree and its branch.
pub fn remove(path: &PathBuf) -> Result<()> {
pub fn remove(path: &Path) -> Result<()> {
let output = Command::new("git")
.arg("-C")
.arg(path)
.args(["worktree", "remove", "--force"])
.arg(path)
.output()
@@ -70,8 +87,10 @@ pub fn list() -> Result<Vec<String>> {
Ok(worktrees)
}
fn get_current_branch() -> Result<String> {
fn get_current_branch(repo_root: &Path) -> Result<String> {
let output = Command::new("git")
.arg("-C")
.arg(repo_root)
.args(["rev-parse", "--abbrev-ref", "HEAD"])
.output()
.context("Failed to get current branch")?;