feat(startup): wire startup repo path from CLI to UI

The app now supports opening a repository at startup by
reading a --repo argument and passing the path to the UI.
A new startup repository state is managed in the backend and
exposed via a tauri command and event, enabling the frontend
to auto-open the repository when ready. The UI adds a small
startup flow with retry handling to ensure the path is
consumed once available.
- Reads --repo or --repo=PATH and resolves relative paths
- Signals UI to open startup repo via open-startup-repository
- Frontend retries opening the repo until it succeeds
This commit is contained in:
2026-08-16 15:54:54 +02:00
parent 25d7f27d7a
commit 993179c25e
2 changed files with 126 additions and 3 deletions
+86 -3
View File
@@ -31,9 +31,79 @@ use git::{
stash_drop, stash_pop, stash_push, undo_last_commit, unlock_worktree, unstage_files,
update_remote,
};
use tauri::Manager;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use tauri::{Emitter, Manager};
use telemetry::{emit_frontend_log, emit_frontend_span, set_telemetry_enabled};
struct StartupRepository(Mutex<Option<String>>);
fn repository_path_from_args(args: impl IntoIterator<Item = String>, cwd: &Path) -> Option<String> {
let mut args = args.into_iter();
let mut repository = None;
while let Some(arg) = args.next() {
if arg == "--repo" {
repository = args.next();
break;
}
if let Some(path) = arg.strip_prefix("--repo=") {
repository = Some(path.to_string());
break;
}
if !arg.starts_with('-') {
repository = Some(arg);
break;
}
}
repository.filter(|path| !path.trim().is_empty()).map(|path| {
let path = PathBuf::from(path);
if path.is_absolute() {
path
} else {
cwd.join(path)
}
.to_string_lossy()
.into_owned()
})
}
#[cfg(test)]
mod startup_repository_tests {
use super::repository_path_from_args;
use std::path::Path;
#[test]
fn accepts_direct_relative_repository_path() {
let path = repository_path_from_args(["projects/repo".to_string()], Path::new("/home/user"));
assert_eq!(path.as_deref(), Some("/home/user/projects/repo"));
}
#[test]
fn accepts_repo_option() {
let path = repository_path_from_args(
["--repo".to_string(), "/projects/repo".to_string()],
Path::new("/home/user"),
);
assert_eq!(path.as_deref(), Some("/projects/repo"));
}
#[test]
fn accepts_repo_equals_option() {
let path = repository_path_from_args(
["--repo=projects/repo".to_string()],
Path::new("/home/user"),
);
assert_eq!(path.as_deref(), Some("/home/user/projects/repo"));
}
}
#[tauri::command]
fn take_startup_repository(state: tauri::State<'_, StartupRepository>) -> Option<String> {
state.0.lock().ok()?.take()
}
struct ConsoleLogger;
impl log::Log for ConsoleLogger {
@@ -96,8 +166,19 @@ async fn main() {
return;
}
let startup_repository = repository_path_from_args(
std::env::args().skip(1),
&std::env::current_dir().unwrap_or_default(),
);
let builder = tauri::Builder::default()
.plugin(tauri_plugin_single_instance::init(|app, _, _| {
.plugin(tauri_plugin_single_instance::init(|app, args, cwd| {
if let Some(path) = repository_path_from_args(args.into_iter().skip(1), Path::new(&cwd)) {
if let Ok(mut pending) = app.state::<StartupRepository>().0.lock() {
*pending = Some(path);
}
let _ = app.emit("open-startup-repository", ());
}
#[cfg(desktop)]
let _ = app
.get_webview_window("main")
@@ -112,6 +193,7 @@ async fn main() {
})
.build(),
)
.manage(StartupRepository(Mutex::new(startup_repository)))
.manage(SearchCancellationState::default())
.manage(commit_ai::CommitAiEngine::new())
.plugin(tauri_plugin_dialog::init());
@@ -226,7 +308,8 @@ async fn main() {
close_splashscreen,
set_telemetry_enabled,
emit_frontend_log,
emit_frontend_span
emit_frontend_span,
take_startup_repository
])
.run(tauri::generate_context!())
.expect("error while running tauri application");