Skip to content

Commit 906b4cb

Browse files
committed
fix(tui): make agent creation fully automated when using AI generation
Previously, when creating an agent via the /agents command with AI-assisted generation, the TUI would: 1. Show a toast notification 2. Put 'Create an agent with this description: ...' in the input box 3. Exit interactive mode, requiring user to manually submit This was confusing and broke the expected flow. Users wanted the agent to be generated automatically without additional user interaction. Changes: - Add ToolEvent::AgentGenerated and ToolEvent::AgentGenerationFailed events - Spawn async task in handle_inline_form_submission for AI generation - Call AgentGenerator directly to generate agent configuration - Save agent to disk automatically - Navigate back to /agents list with success message - Remove toast notification during generation - Remove the 'Create an agent...' input text injection Now the flow is: 1. User enters description in the AI form 2. Agent is generated via LLM in background 3. Agent is saved to .cortex/agents/ or ~/.cortex/agents/ 4. Success toast shown and /agents list refreshed with new agent
1 parent a7d499e commit 906b4cb

File tree

2 files changed

+152
-12
lines changed

2 files changed

+152
-12
lines changed

cortex-tui/src/events.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,20 @@ pub enum ToolEvent {
6767
/// Todo items: (content, status) where status is "pending", "in_progress", or "completed"
6868
todos: Vec<(String, String)>,
6969
},
70+
/// Agent generation completed successfully.
71+
AgentGenerated {
72+
/// Agent name/identifier
73+
name: String,
74+
/// Path where agent was saved
75+
path: String,
76+
/// Location type (project or global)
77+
location: String,
78+
},
79+
/// Agent generation failed.
80+
AgentGenerationFailed {
81+
/// Error message
82+
error: String,
83+
},
7084
}
7185

7286
/// Events from subagent executions.

cortex-tui/src/runner/event_loop.rs

Lines changed: 138 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3647,6 +3647,48 @@ impl EventLoop {
36473647
}
36483648
});
36493649
}
3650+
3651+
ToolEvent::AgentGenerated {
3652+
name,
3653+
path,
3654+
location,
3655+
} => {
3656+
// Agent was generated and saved successfully
3657+
tracing::info!(
3658+
"Agent generated: {} (location: {}, path: {})",
3659+
name,
3660+
location,
3661+
path
3662+
);
3663+
3664+
// Show success message
3665+
self.app_state
3666+
.toasts
3667+
.success(format!("Agent @{} created!", name));
3668+
3669+
// Add system message about the created agent
3670+
self.inject_agent_created_event(&name);
3671+
3672+
// Refresh the agents list in interactive mode
3673+
let cwd = std::env::current_dir().ok();
3674+
let terminal_height = self.app_state.terminal_size.1;
3675+
let interactive = crate::interactive::builders::build_agents_selector(
3676+
cwd.as_deref(),
3677+
Some(terminal_height),
3678+
);
3679+
self.app_state.enter_interactive_mode(interactive);
3680+
}
3681+
3682+
ToolEvent::AgentGenerationFailed { error } => {
3683+
// Agent generation failed
3684+
tracing::error!("Agent generation failed: {}", error);
3685+
3686+
// Show error message
3687+
self.app_state.toasts.error(&error);
3688+
3689+
// Exit interactive mode (user can try again with /agents)
3690+
self.app_state.exit_interactive_mode();
3691+
}
36503692
}
36513693
}
36523694

@@ -6666,6 +6708,7 @@ impl EventLoop {
66666708
}
66676709
action if action.starts_with("agent-ai-create:") => {
66686710
// AI-assisted agent creation from inline form
6711+
// Fully automated: generate agent with LLM and save directly
66696712
let location_str = action.strip_prefix("agent-ai-create:").unwrap_or("project");
66706713
let description = values.get("description").cloned().unwrap_or_default();
66716714

@@ -6683,21 +6726,104 @@ impl EventLoop {
66836726
_ => AgentLocation::Project,
66846727
};
66856728

6686-
// Store location for later use
6687-
self.app_state.agent_creation_location = Some(location);
6688-
// Set mode to AI and store the description for processing
6689-
self.app_state.agent_creation_mode = Some("ai".to_string());
6729+
// Spawn async task for AI agent generation
6730+
let tx = self.tool_event_tx.clone();
6731+
let location_clone = location;
6732+
let description_clone = description.clone();
6733+
6734+
tokio::spawn(async move {
6735+
use cortex_engine::agent::AgentGenerator;
6736+
6737+
// Generate agent configuration using AI
6738+
let generator = AgentGenerator::new().with_model("gpt-4o");
6739+
6740+
match generator.generate(&description_clone).await {
6741+
Ok(generated) => {
6742+
// Determine save directory based on location
6743+
let dir = match location_clone {
6744+
AgentLocation::Project => {
6745+
match std::env::current_dir() {
6746+
Ok(cwd) => cwd.join(".cortex/agents"),
6747+
Err(e) => {
6748+
let _ = tx.send(crate::events::ToolEvent::AgentGenerationFailed {
6749+
error: format!("Could not determine current directory: {}", e),
6750+
}).await;
6751+
return;
6752+
}
6753+
}
6754+
}
6755+
AgentLocation::Global => {
6756+
match dirs::home_dir() {
6757+
Some(home) => home.join(".cortex/agents"),
6758+
None => {
6759+
let _ = tx.send(crate::events::ToolEvent::AgentGenerationFailed {
6760+
error: "Could not determine home directory".to_string(),
6761+
}).await;
6762+
return;
6763+
}
6764+
}
6765+
}
6766+
};
66906767

6691-
// Insert the description into the input box and trigger AI generation
6692-
let prompt = format!("Create an agent with this description: {}", description);
6693-
self.app_state.input.set_text(&prompt);
6768+
// Create directory if it doesn't exist
6769+
if let Err(e) = std::fs::create_dir_all(&dir) {
6770+
let _ = tx
6771+
.send(crate::events::ToolEvent::AgentGenerationFailed {
6772+
error: format!("Failed to create agents directory: {}", e),
6773+
})
6774+
.await;
6775+
return;
6776+
}
66946777

6695-
self.app_state
6696-
.toasts
6697-
.info("Generating agent configuration...");
6778+
// Save agent file
6779+
let filepath = dir.join(generated.filename());
6780+
let content = generated.to_markdown();
66986781

6699-
// Exit interactive mode - the description will be processed
6700-
false
6782+
if let Err(e) = std::fs::write(&filepath, &content) {
6783+
let _ = tx
6784+
.send(crate::events::ToolEvent::AgentGenerationFailed {
6785+
error: format!("Failed to save agent file: {}", e),
6786+
})
6787+
.await;
6788+
return;
6789+
}
6790+
6791+
// Send success event
6792+
let _ = tx
6793+
.send(crate::events::ToolEvent::AgentGenerated {
6794+
name: generated.identifier.clone(),
6795+
path: filepath.display().to_string(),
6796+
location: match location_clone {
6797+
AgentLocation::Project => "project".to_string(),
6798+
AgentLocation::Global => "global".to_string(),
6799+
},
6800+
})
6801+
.await;
6802+
}
6803+
Err(e) => {
6804+
let _ = tx
6805+
.send(crate::events::ToolEvent::AgentGenerationFailed {
6806+
error: format!("Failed to generate agent: {}", e),
6807+
})
6808+
.await;
6809+
}
6810+
}
6811+
});
6812+
6813+
// Clear creation state
6814+
self.app_state.agent_creation_location = None;
6815+
self.app_state.agent_creation_mode = None;
6816+
6817+
// Stay in interactive mode - will be updated when generation completes
6818+
// Show a loading state in the agents panel
6819+
let cwd = std::env::current_dir().ok();
6820+
let terminal_height = self.app_state.terminal_size.1;
6821+
let interactive = crate::interactive::builders::build_agents_selector(
6822+
cwd.as_deref(),
6823+
Some(terminal_height),
6824+
);
6825+
self.app_state.enter_interactive_mode(interactive);
6826+
true // Stay in interactive mode showing agents list
67016827
}
67026828
_ => {
67036829
tracing::warn!("Unhandled inline form submission: {}", action_id);

0 commit comments

Comments
 (0)