Fix /resume to show all sessions and use human-readable timestamps
- Change run_autonomous to return Agent instead of () so session
continuation is properly saved in accumulative mode
- Update format_session_time to show relative times ("2 hours ago",
"yesterday") for recent sessions and dates for older ones
- Handle Ctrl+C cancellation gracefully with informative message
This commit is contained in:
@@ -512,6 +512,9 @@ pub async fn run() -> Result<()> {
|
||||
// Then load README for project context
|
||||
let readme_content = read_project_readme(&workspace_dir);
|
||||
|
||||
// Load project memory if available
|
||||
let memory_content = read_project_memory(&workspace_dir);
|
||||
|
||||
// Create project model
|
||||
let project = if cli.autonomous {
|
||||
if let Some(requirements_text) = &cli.requirements {
|
||||
@@ -587,12 +590,23 @@ pub async fn run() -> Result<()> {
|
||||
// Initialize agent
|
||||
// ui_writer will be created conditionally based on machine mode
|
||||
|
||||
// Combine AGENTS.md and README content if both exist
|
||||
let combined_content = match (agents_content.clone(), readme_content.clone()) {
|
||||
(Some(agents), Some(readme)) => Some(format!("{}\n\n{}", agents, readme)),
|
||||
(Some(agents), None) => Some(agents),
|
||||
(None, Some(readme)) => Some(readme),
|
||||
(None, None) => None,
|
||||
// Combine AGENTS.md, README, and memory content if they exist
|
||||
let combined_content = {
|
||||
let mut parts = Vec::new();
|
||||
if let Some(agents) = agents_content.clone() {
|
||||
parts.push(agents);
|
||||
}
|
||||
if let Some(readme) = readme_content.clone() {
|
||||
parts.push(readme);
|
||||
}
|
||||
if let Some(memory) = memory_content.clone() {
|
||||
parts.push(memory);
|
||||
}
|
||||
if parts.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(parts.join("\n\n"))
|
||||
}
|
||||
};
|
||||
|
||||
// Execute task, autonomous mode, or start interactive mode based on machine mode
|
||||
@@ -1175,18 +1189,25 @@ async fn run_accumulative_mode(
|
||||
cli.max_turns,
|
||||
cli.quiet,
|
||||
cli.codebase_fast_start.clone(),
|
||||
) => result,
|
||||
) => result.map(Some),
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
output.print("\n⚠️ Autonomous run cancelled by user (Ctrl+C)");
|
||||
Ok(())
|
||||
// Agent was moved into run_autonomous and is now dropped
|
||||
// We can't save continuation here, but the next iteration will create a new agent
|
||||
Ok(None)
|
||||
}
|
||||
};
|
||||
|
||||
match autonomous_result {
|
||||
Ok(_) => {
|
||||
Ok(Some(_returned_agent)) => {
|
||||
// Session continuation was already saved by run_autonomous
|
||||
output.print("");
|
||||
output.print("✅ Autonomous run completed");
|
||||
}
|
||||
Ok(None) => {
|
||||
// Ctrl+C case - agent was dropped, continuation not saved
|
||||
output.print(" (session continuation not saved due to cancellation)");
|
||||
}
|
||||
Err(e) => {
|
||||
output.print("");
|
||||
output.print(&format!("❌ Autonomous run failed: {}", e));
|
||||
@@ -1280,7 +1301,7 @@ async fn run_with_console_mode(
|
||||
// Execute task, autonomous mode, or start interactive mode
|
||||
if cli.autonomous {
|
||||
// Autonomous mode with coach-player feedback loop
|
||||
run_autonomous(
|
||||
let _agent = run_autonomous(
|
||||
agent,
|
||||
project,
|
||||
cli.show_prompt,
|
||||
@@ -1447,6 +1468,30 @@ fn read_project_readme(workspace_dir: &Path) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Read project memory if available
|
||||
fn read_project_memory(workspace_dir: &Path) -> Option<String> {
|
||||
let memory_path = workspace_dir.join(".g3").join("memory.md");
|
||||
|
||||
if memory_path.exists() {
|
||||
match std::fs::read_to_string(&memory_path) {
|
||||
Ok(content) => {
|
||||
let size = if content.len() < 1000 {
|
||||
format!("{} chars", content.len())
|
||||
} else {
|
||||
format!("{:.1}k chars", content.len() as f64 / 1000.0)
|
||||
};
|
||||
Some(format!(
|
||||
"🧠 Project Memory ({}):\n\n{}",
|
||||
size, content
|
||||
))
|
||||
}
|
||||
Err(_) => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the main heading or title from README content
|
||||
fn extract_readme_heading(readme_content: &str) -> Option<String> {
|
||||
// Find the README section in the combined content
|
||||
@@ -1594,6 +1639,7 @@ async fn run_interactive<W: UiWriter>(
|
||||
// Check what was loaded
|
||||
let has_agents = content.contains("Agent Configuration");
|
||||
let has_readme = content.contains("Project README");
|
||||
let has_memory = content.contains("Project Memory");
|
||||
|
||||
if has_agents {
|
||||
print!(
|
||||
@@ -1615,6 +1661,14 @@ async fn run_interactive<W: UiWriter>(
|
||||
ResetColor
|
||||
);
|
||||
}
|
||||
|
||||
if has_memory {
|
||||
print!(
|
||||
"{}🧠 Project memory loaded{}\n",
|
||||
SetForegroundColor(Color::DarkGrey),
|
||||
ResetColor
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Display workspace path
|
||||
@@ -2354,7 +2408,7 @@ async fn run_autonomous(
|
||||
max_turns: usize,
|
||||
quiet: bool,
|
||||
codebase_fast_start: Option<PathBuf>,
|
||||
) -> Result<()> {
|
||||
) -> Result<Agent<ConsoleUiWriter>> {
|
||||
let start_time = std::time::Instant::now();
|
||||
let output = SimpleOutput::new();
|
||||
let mut turn_metrics: Vec<TurnMetrics> = Vec::new();
|
||||
@@ -2411,7 +2465,7 @@ async fn run_autonomous(
|
||||
output.print(&generate_turn_histogram(&turn_metrics));
|
||||
output.print(&"=".repeat(60));
|
||||
|
||||
return Ok(());
|
||||
return Ok(agent);
|
||||
}
|
||||
|
||||
// Read requirements
|
||||
@@ -2453,7 +2507,7 @@ async fn run_autonomous(
|
||||
output.print(&generate_turn_histogram(&turn_metrics));
|
||||
output.print(&"=".repeat(60));
|
||||
|
||||
return Ok(());
|
||||
return Ok(agent);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3076,5 +3130,5 @@ Remember: Be clear in your review and concise in your feedback. APPROVE iff the
|
||||
// Save session continuation for resume capability
|
||||
agent.save_session_continuation(None);
|
||||
|
||||
Ok(())
|
||||
Ok(agent)
|
||||
}
|
||||
|
||||
@@ -434,7 +434,32 @@ pub fn format_session_time(created_at: &str) -> String {
|
||||
match chrono::DateTime::parse_from_rfc3339(created_at) {
|
||||
Ok(dt) => {
|
||||
let local: chrono::DateTime<chrono::Local> = dt.into();
|
||||
local.format("%Y-%m-%d %H:%M").to_string()
|
||||
let now = chrono::Local::now();
|
||||
let duration = now.signed_duration_since(local);
|
||||
|
||||
// Show relative time for recent sessions, absolute for older ones
|
||||
if duration.num_minutes() < 1 {
|
||||
"just now".to_string()
|
||||
} else if duration.num_minutes() < 60 {
|
||||
format!("{} min ago", duration.num_minutes())
|
||||
} else if duration.num_hours() < 24 {
|
||||
let hours = duration.num_hours();
|
||||
if hours == 1 {
|
||||
"1 hour ago".to_string()
|
||||
} else {
|
||||
format!("{} hours ago", hours)
|
||||
}
|
||||
} else if duration.num_days() < 7 {
|
||||
let days = duration.num_days();
|
||||
if days == 1 {
|
||||
"yesterday".to_string()
|
||||
} else {
|
||||
format!("{} days ago", days)
|
||||
}
|
||||
} else {
|
||||
// For older sessions, show the date
|
||||
local.format("%b %d, %Y").to_string()
|
||||
}
|
||||
}
|
||||
Err(_) => created_at.to_string(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user