diff --git a/src/cli.rs b/src/cli.rs index c740ac3..6f956d9 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -298,8 +298,8 @@ pub enum Command { /// Arguments shared by start and restart. #[derive(Args, Debug, Clone, Default)] pub struct StartArgs { - /// Agent name, alias, or group: - pub agent: String, + /// Agent name, alias, or group: (defaults to the project's default agent) + pub agent: Option, /// Run detached in the background; output goes to the agent log file #[arg(short = 'b', long, conflicts_with = "foreground", action = ArgAction::SetTrue)] pub background: bool, diff --git a/src/commands/run_cmd.rs b/src/commands/run_cmd.rs index ad6583b..aae8e4e 100644 --- a/src/commands/run_cmd.rs +++ b/src/commands/run_cmd.rs @@ -11,10 +11,25 @@ use std::ffi::OsString; use std::path::PathBuf; use std::process::Command; +/// Resolve the start target: the explicit agent/alias/group when given, +/// otherwise the default agent of the project containing cwd (issue #30). +pub fn resolve_start_target(app: &App, cwd: &std::path::Path, agent: Option<&str>) -> Result { + if let Some(a) = agent.filter(|s| !s.is_empty()) { + return Ok(a.to_string()); + } + crate::projects::default_agent_for_dir(app, cwd).ok_or_else(|| { + anyhow!( + "no agent given — set default_agent in this project's profile ('am init' does it) or pass an agent name" + ) + }) +} + pub fn start(app: &App, opts: &StartArgs) -> Result { + let cwd = std::env::current_dir().unwrap_or_default(); + let target = resolve_start_target(app, &cwd, opts.agent.as_deref())?; let extra_env = parse_env_list(&opts.env)?; let extra_args = parse_extra_args(&opts.args); - if let Some(group) = crate::catalog::Catalog::parse_group_selector(&opts.agent) { + if let Some(group) = crate::catalog::Catalog::parse_group_selector(&target) { let members = app.catalog.group_members(group); if members.is_empty() { bail!("unknown group '{group}'"); @@ -24,7 +39,7 @@ pub fn start(app: &App, opts: &StartArgs) -> Result { } return Ok(0); } - let agent = require_agent(app, &opts.agent)?; + let agent = require_agent(app, &target)?; start_one(app, agent, &extra_args, &extra_env, opts.notify, opts.background) } @@ -197,7 +212,9 @@ fn stop_one(app: &App, agent: &AgentDef, force: bool, timeout: u64) -> Result) -> Result { let timeout = timeout.unwrap_or_else(|| app.config.settings.stop_timeout()); - if let Some(group) = crate::catalog::Catalog::parse_group_selector(&opts.agent) { + let cwd = std::env::current_dir().unwrap_or_default(); + let target = resolve_start_target(app, &cwd, opts.agent.as_deref())?; + if let Some(group) = crate::catalog::Catalog::parse_group_selector(&target) { let members = app.catalog.group_members(group); if members.is_empty() { bail!("unknown group '{group}'"); @@ -212,7 +229,7 @@ pub fn restart(app: &App, opts: &StartArgs, force: bool, timeout: Option) - } return Ok(0); } - let agent = require_agent(app, &opts.agent)?; + let agent = require_agent(app, &target)?; stop_one(app, agent, force, timeout)?; let extra_env = parse_env_list(&opts.env)?; let extra_args = parse_extra_args(&opts.args); diff --git a/src/repl.rs b/src/repl.rs index 99152a7..ca2f0f1 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1283,7 +1283,7 @@ fn handle_line( agent: need("agent name")?, }, "start" => Command::Start(StartArgs { - agent: need("agent name")?, + agent: rest.first().cloned(), background: flag("--background") || flag("-b"), ..Default::default() }), @@ -1294,7 +1294,7 @@ fn handle_line( }, "restart" => Command::Restart { start: StartArgs { - agent: need("agent name")?, + agent: rest.first().cloned(), background: flag("--background") || flag("-b"), ..Default::default() }, diff --git a/tests/instrumentation_test.rs b/tests/instrumentation_test.rs index 9e3add6..74b7b26 100644 --- a/tests/instrumentation_test.rs +++ b/tests/instrumentation_test.rs @@ -100,7 +100,7 @@ fn start_and_stop_emit_a_matching_pair() { app.state.set(&entry).unwrap(); let opts = StartArgs { - agent: "sleeper".to_string(), + agent: Some("sleeper".to_string()), background: true, ..Default::default() }; diff --git a/tests/projects_test.rs b/tests/projects_test.rs index c2d1b8e..12ef7b2 100644 --- a/tests/projects_test.rs +++ b/tests/projects_test.rs @@ -75,5 +75,18 @@ projects: ); // Hors du projet : pas de profil. assert!(projects::default_agent_for_dir(&app, &std::env::temp_dir()).is_none()); + + // am start contextuel (issue #30) : résolution pure sans changer de cwd. + use agent_manager::commands::run_cmd; + assert_eq!( + run_cmd::resolve_start_target(&app, &sub, None).unwrap(), + "claude-code" + ); + assert_eq!( + run_cmd::resolve_start_target(&app, &sub, Some("aider")).unwrap(), + "aider" + ); + assert!(run_cmd::resolve_start_target(&app, &std::env::temp_dir(), None).is_err()); + let _ = std::fs::remove_dir_all(&dir); }