feat: goose cli to track directories, allow to resume projects in location (#2503)
This commit is contained in:
@@ -7,6 +7,7 @@ use crate::commands::bench::agent_generator;
|
|||||||
use crate::commands::configure::handle_configure;
|
use crate::commands::configure::handle_configure;
|
||||||
use crate::commands::info::handle_info;
|
use crate::commands::info::handle_info;
|
||||||
use crate::commands::mcp::run_server;
|
use crate::commands::mcp::run_server;
|
||||||
|
use crate::commands::project::{handle_project_default, handle_projects_interactive};
|
||||||
use crate::commands::recipe::{handle_deeplink, handle_validate};
|
use crate::commands::recipe::{handle_deeplink, handle_validate};
|
||||||
use crate::commands::session::{handle_session_list, handle_session_remove};
|
use crate::commands::session::{handle_session_list, handle_session_remove};
|
||||||
use crate::logging::setup_logging;
|
use crate::logging::setup_logging;
|
||||||
@@ -250,6 +251,14 @@ enum Command {
|
|||||||
builtins: Vec<String>,
|
builtins: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Open the last project directory
|
||||||
|
#[command(about = "Open the last project directory", visible_alias = "p")]
|
||||||
|
Project {},
|
||||||
|
|
||||||
|
/// List recent project directories
|
||||||
|
#[command(about = "List recent project directories", visible_alias = "ps")]
|
||||||
|
Projects,
|
||||||
|
|
||||||
/// Execute commands from an instruction file
|
/// Execute commands from an instruction file
|
||||||
#[command(about = "Execute commands from an instruction file or stdin")]
|
#[command(about = "Execute commands from an instruction file or stdin")]
|
||||||
Run {
|
Run {
|
||||||
@@ -405,6 +414,11 @@ struct InputConfig {
|
|||||||
pub async fn cli() -> Result<()> {
|
pub async fn cli() -> Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
// Track the current directory in projects.json
|
||||||
|
if let Err(e) = crate::project_tracker::update_project_tracker(None, None) {
|
||||||
|
eprintln!("Warning: Failed to update project tracker: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Some(Command::Configure {}) => {
|
Some(Command::Configure {}) => {
|
||||||
let _ = handle_configure().await;
|
let _ = handle_configure().await;
|
||||||
@@ -468,6 +482,16 @@ pub async fn cli() -> Result<()> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Some(Command::Project {}) => {
|
||||||
|
// Default behavior: offer to resume the last project
|
||||||
|
handle_project_default()?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Some(Command::Projects) => {
|
||||||
|
// Interactive project selection
|
||||||
|
handle_projects_interactive()?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
Some(Command::Run {
|
Some(Command::Run {
|
||||||
instructions,
|
instructions,
|
||||||
input_text,
|
input_text,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ pub mod bench;
|
|||||||
pub mod configure;
|
pub mod configure;
|
||||||
pub mod info;
|
pub mod info;
|
||||||
pub mod mcp;
|
pub mod mcp;
|
||||||
|
pub mod project;
|
||||||
pub mod recipe;
|
pub mod recipe;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
|||||||
@@ -0,0 +1,307 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use chrono::DateTime;
|
||||||
|
use cliclack::{self, intro, outro};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::project_tracker::ProjectTracker;
|
||||||
|
|
||||||
|
/// Format a DateTime for display
|
||||||
|
fn format_date(date: DateTime<chrono::Utc>) -> String {
|
||||||
|
// Format: "2025-05-08 18:15:30"
|
||||||
|
date.format("%Y-%m-%d %H:%M:%S").to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle the default project command
|
||||||
|
///
|
||||||
|
/// Offers options to resume the most recently accessed project
|
||||||
|
pub fn handle_project_default() -> Result<()> {
|
||||||
|
let tracker = ProjectTracker::load()?;
|
||||||
|
let mut projects = tracker.list_projects();
|
||||||
|
|
||||||
|
if projects.is_empty() {
|
||||||
|
// If no projects exist, just start a new one in the current directory
|
||||||
|
println!("No previous projects found. Starting a new session in the current directory.");
|
||||||
|
let mut command = std::process::Command::new("goose");
|
||||||
|
command.arg("session");
|
||||||
|
let status = command.status()?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
println!("Failed to run Goose. Exit code: {:?}", status.code());
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort projects by last_accessed (newest first)
|
||||||
|
projects.sort_by(|a, b| b.last_accessed.cmp(&a.last_accessed));
|
||||||
|
|
||||||
|
// Get the most recent project
|
||||||
|
let project = &projects[0];
|
||||||
|
let project_dir = &project.path;
|
||||||
|
|
||||||
|
// Check if the directory exists
|
||||||
|
if !Path::new(project_dir).exists() {
|
||||||
|
println!(
|
||||||
|
"Most recent project directory '{}' no longer exists.",
|
||||||
|
project_dir
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format the path for display
|
||||||
|
let path = Path::new(project_dir);
|
||||||
|
let components: Vec<_> = path.components().collect();
|
||||||
|
let len = components.len();
|
||||||
|
let short_path = if len <= 2 {
|
||||||
|
project_dir.clone()
|
||||||
|
} else {
|
||||||
|
let mut path_str = String::new();
|
||||||
|
path_str.push_str("...");
|
||||||
|
for component in components.iter().skip(len - 2) {
|
||||||
|
path_str.push('/');
|
||||||
|
path_str.push_str(component.as_os_str().to_string_lossy().as_ref());
|
||||||
|
}
|
||||||
|
path_str
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ask the user what they want to do
|
||||||
|
let _ = intro("Goose Project Manager");
|
||||||
|
|
||||||
|
let current_dir = std::env::current_dir()?;
|
||||||
|
let current_dir_display = current_dir.display();
|
||||||
|
|
||||||
|
let choice = cliclack::select("Choose an option:")
|
||||||
|
.item(
|
||||||
|
"resume",
|
||||||
|
format!("Resume project with session: {}", short_path),
|
||||||
|
"Continue with the previous session",
|
||||||
|
)
|
||||||
|
.item(
|
||||||
|
"fresh",
|
||||||
|
format!("Resume project with fresh session: {}", short_path),
|
||||||
|
"Change to the project directory but start a new session",
|
||||||
|
)
|
||||||
|
.item(
|
||||||
|
"new",
|
||||||
|
format!(
|
||||||
|
"Start new project in current directory: {}",
|
||||||
|
current_dir_display
|
||||||
|
),
|
||||||
|
"Stay in the current directory and start a new session",
|
||||||
|
)
|
||||||
|
.interact()?;
|
||||||
|
|
||||||
|
match choice {
|
||||||
|
"resume" => {
|
||||||
|
let _ = outro(format!("Changing to directory: {}", project_dir));
|
||||||
|
|
||||||
|
// Get the session ID if available
|
||||||
|
let session_id = project.last_session_id.clone();
|
||||||
|
|
||||||
|
// Change to the project directory
|
||||||
|
std::env::set_current_dir(project_dir)?;
|
||||||
|
|
||||||
|
// Build the command to run Goose
|
||||||
|
let mut command = std::process::Command::new("goose");
|
||||||
|
command.arg("session");
|
||||||
|
|
||||||
|
if let Some(id) = session_id {
|
||||||
|
command.arg("--name").arg(&id).arg("--resume");
|
||||||
|
println!("Resuming session: {}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the command
|
||||||
|
let status = command.status()?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
println!("Failed to run Goose. Exit code: {:?}", status.code());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"fresh" => {
|
||||||
|
let _ = outro(format!(
|
||||||
|
"Changing to directory: {} with a fresh session",
|
||||||
|
project_dir
|
||||||
|
));
|
||||||
|
|
||||||
|
// Change to the project directory
|
||||||
|
std::env::set_current_dir(project_dir)?;
|
||||||
|
|
||||||
|
// Build the command to run Goose with a fresh session
|
||||||
|
let mut command = std::process::Command::new("goose");
|
||||||
|
command.arg("session");
|
||||||
|
|
||||||
|
// Execute the command
|
||||||
|
let status = command.status()?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
println!("Failed to run Goose. Exit code: {:?}", status.code());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"new" => {
|
||||||
|
let _ = outro("Starting a new session in the current directory");
|
||||||
|
|
||||||
|
// Build the command to run Goose
|
||||||
|
let mut command = std::process::Command::new("goose");
|
||||||
|
command.arg("session");
|
||||||
|
|
||||||
|
// Execute the command
|
||||||
|
let status = command.status()?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
println!("Failed to run Goose. Exit code: {:?}", status.code());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let _ = outro("Operation canceled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle the interactive projects command
|
||||||
|
///
|
||||||
|
/// Shows a list of projects and lets the user select one to resume
|
||||||
|
pub fn handle_projects_interactive() -> Result<()> {
|
||||||
|
let tracker = ProjectTracker::load()?;
|
||||||
|
let mut projects = tracker.list_projects();
|
||||||
|
|
||||||
|
if projects.is_empty() {
|
||||||
|
println!("No projects found.");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort projects by last_accessed (newest first)
|
||||||
|
projects.sort_by(|a, b| b.last_accessed.cmp(&a.last_accessed));
|
||||||
|
|
||||||
|
// Format project paths for display
|
||||||
|
let project_choices: Vec<(String, String)> = projects
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, project)| {
|
||||||
|
let path = Path::new(&project.path);
|
||||||
|
let components: Vec<_> = path.components().collect();
|
||||||
|
let len = components.len();
|
||||||
|
let short_path = if len <= 2 {
|
||||||
|
project.path.clone()
|
||||||
|
} else {
|
||||||
|
let mut path_str = String::new();
|
||||||
|
path_str.push_str("...");
|
||||||
|
for component in components.iter().skip(len - 2) {
|
||||||
|
path_str.push('/');
|
||||||
|
path_str.push_str(component.as_os_str().to_string_lossy().as_ref());
|
||||||
|
}
|
||||||
|
path_str
|
||||||
|
};
|
||||||
|
|
||||||
|
// Include last instruction if available (truncated)
|
||||||
|
let instruction_preview =
|
||||||
|
project
|
||||||
|
.last_instruction
|
||||||
|
.as_ref()
|
||||||
|
.map_or(String::new(), |instr| {
|
||||||
|
let truncated = if instr.len() > 40 {
|
||||||
|
format!("{}...", &instr[0..37])
|
||||||
|
} else {
|
||||||
|
instr.clone()
|
||||||
|
};
|
||||||
|
format!(" [{}]", truncated)
|
||||||
|
});
|
||||||
|
|
||||||
|
let formatted_date = format_date(project.last_accessed);
|
||||||
|
(
|
||||||
|
format!("{}", i + 1), // Value to return
|
||||||
|
format!("{} ({}){}", short_path, formatted_date, instruction_preview), // Display text with instruction
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Let the user select a project
|
||||||
|
let _ = intro("Goose Project Manager");
|
||||||
|
let mut select = cliclack::select("Select a project:");
|
||||||
|
|
||||||
|
// Add each project as an option
|
||||||
|
for (value, display) in &project_choices {
|
||||||
|
select = select.item(value, display, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a cancel option
|
||||||
|
let cancel_value = String::from("cancel");
|
||||||
|
select = select.item(&cancel_value, "Cancel", "Don't resume any project");
|
||||||
|
|
||||||
|
let selected = select.interact()?;
|
||||||
|
|
||||||
|
if selected == "cancel" {
|
||||||
|
let _ = outro("Project selection canceled.");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the selected index
|
||||||
|
let index = selected.parse::<usize>().unwrap_or(0);
|
||||||
|
if index == 0 || index > projects.len() {
|
||||||
|
let _ = outro("Invalid selection.");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the selected project
|
||||||
|
let project = &projects[index - 1];
|
||||||
|
let project_dir = &project.path;
|
||||||
|
|
||||||
|
// Check if the directory exists
|
||||||
|
if !Path::new(project_dir).exists() {
|
||||||
|
let _ = outro(format!(
|
||||||
|
"Project directory '{}' no longer exists.",
|
||||||
|
project_dir
|
||||||
|
));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask if the user wants to resume the session or start a new one
|
||||||
|
let session_id = project.last_session_id.clone();
|
||||||
|
let has_previous_session = session_id.is_some();
|
||||||
|
|
||||||
|
// Change to the project directory first
|
||||||
|
std::env::set_current_dir(project_dir)?;
|
||||||
|
let _ = outro(format!("Changed to directory: {}", project_dir));
|
||||||
|
|
||||||
|
// Only ask about resuming if there's a previous session
|
||||||
|
let resume_session = if has_previous_session {
|
||||||
|
let session_choice = cliclack::select("What would you like to do?")
|
||||||
|
.item(
|
||||||
|
"resume",
|
||||||
|
"Resume previous session",
|
||||||
|
"Continue with the previous session",
|
||||||
|
)
|
||||||
|
.item(
|
||||||
|
"new",
|
||||||
|
"Start new session",
|
||||||
|
"Start a fresh session in this project directory",
|
||||||
|
)
|
||||||
|
.interact()?;
|
||||||
|
|
||||||
|
session_choice == "resume"
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build the command to run Goose
|
||||||
|
let mut command = std::process::Command::new("goose");
|
||||||
|
command.arg("session");
|
||||||
|
|
||||||
|
if resume_session {
|
||||||
|
if let Some(id) = session_id {
|
||||||
|
command.arg("--name").arg(&id).arg("--resume");
|
||||||
|
println!("Resuming session: {}", id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("Starting new session");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the command
|
||||||
|
let status = command.status()?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
println!("Failed to run Goose. Exit code: {:?}", status.code());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ use once_cell::sync::Lazy;
|
|||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
|
pub mod project_tracker;
|
||||||
pub mod recipes;
|
pub mod recipes;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
pub mod signal;
|
pub mod signal;
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
use anyhow::{Context, Result};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use etcetera::{choose_app_strategy, AppStrategy};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
/// Structure to track project information
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ProjectInfo {
|
||||||
|
/// The absolute path to the project directory
|
||||||
|
pub path: String,
|
||||||
|
/// Last time the project was accessed
|
||||||
|
pub last_accessed: DateTime<Utc>,
|
||||||
|
/// Last instruction sent to goose (if available)
|
||||||
|
pub last_instruction: Option<String>,
|
||||||
|
/// Last session ID associated with this project
|
||||||
|
pub last_session_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Structure to hold all tracked projects
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ProjectTracker {
|
||||||
|
projects: HashMap<String, ProjectInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Project information with path as a separate field for easier access
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ProjectInfoDisplay {
|
||||||
|
/// The absolute path to the project directory
|
||||||
|
pub path: String,
|
||||||
|
/// Last time the project was accessed
|
||||||
|
pub last_accessed: DateTime<Utc>,
|
||||||
|
/// Last instruction sent to goose (if available)
|
||||||
|
pub last_instruction: Option<String>,
|
||||||
|
/// Last session ID associated with this project
|
||||||
|
pub last_session_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProjectTracker {
|
||||||
|
/// Get the path to the projects.json file
|
||||||
|
fn get_projects_file() -> Result<PathBuf> {
|
||||||
|
let projects_file = choose_app_strategy(crate::APP_STRATEGY.clone())
|
||||||
|
.context("goose requires a home dir")?
|
||||||
|
.in_data_dir("projects.json");
|
||||||
|
|
||||||
|
// Ensure data directory exists
|
||||||
|
if let Some(parent) = projects_file.parent() {
|
||||||
|
if !parent.exists() {
|
||||||
|
fs::create_dir_all(parent)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(projects_file)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the project tracker from the projects.json file
|
||||||
|
pub fn load() -> Result<Self> {
|
||||||
|
let projects_file = Self::get_projects_file()?;
|
||||||
|
|
||||||
|
if projects_file.exists() {
|
||||||
|
let file_content = fs::read_to_string(&projects_file)?;
|
||||||
|
let tracker: ProjectTracker = serde_json::from_str(&file_content)
|
||||||
|
.context("Failed to parse projects.json file")?;
|
||||||
|
Ok(tracker)
|
||||||
|
} else {
|
||||||
|
// If the file doesn't exist, create a new empty tracker
|
||||||
|
Ok(ProjectTracker {
|
||||||
|
projects: HashMap::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Save the project tracker to the projects.json file
|
||||||
|
pub fn save(&self) -> Result<()> {
|
||||||
|
let projects_file = Self::get_projects_file()?;
|
||||||
|
let json = serde_json::to_string_pretty(self)?;
|
||||||
|
fs::write(projects_file, json)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update project information for the current directory
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `project_dir` - The project directory to update
|
||||||
|
/// * `instruction` - Optional instruction that was sent to goose
|
||||||
|
/// * `session_id` - Optional session ID associated with this project
|
||||||
|
pub fn update_project(
|
||||||
|
&mut self,
|
||||||
|
project_dir: &Path,
|
||||||
|
instruction: Option<&str>,
|
||||||
|
session_id: Option<&str>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let dir_str = project_dir.to_string_lossy().to_string();
|
||||||
|
|
||||||
|
// Create or update the project entry
|
||||||
|
let project_info = self.projects.entry(dir_str.clone()).or_insert(ProjectInfo {
|
||||||
|
path: dir_str,
|
||||||
|
last_accessed: Utc::now(),
|
||||||
|
last_instruction: None,
|
||||||
|
last_session_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the last accessed time
|
||||||
|
project_info.last_accessed = Utc::now();
|
||||||
|
|
||||||
|
// Update the last instruction if provided
|
||||||
|
if let Some(instr) = instruction {
|
||||||
|
project_info.last_instruction = Some(instr.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the session ID if provided
|
||||||
|
if let Some(id) = session_id {
|
||||||
|
project_info.last_session_id = Some(id.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List all tracked projects
|
||||||
|
///
|
||||||
|
/// Returns a vector of ProjectInfoDisplay objects
|
||||||
|
pub fn list_projects(&self) -> Vec<ProjectInfoDisplay> {
|
||||||
|
self.projects
|
||||||
|
.values()
|
||||||
|
.map(|info| ProjectInfoDisplay {
|
||||||
|
path: info.path.clone(),
|
||||||
|
last_accessed: info.last_accessed,
|
||||||
|
last_instruction: info.last_instruction.clone(),
|
||||||
|
last_session_id: info.last_session_id.clone(),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the project tracker with the current directory and optional instruction
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `instruction` - Optional instruction that was sent to goose
|
||||||
|
/// * `session_id` - Optional session ID associated with this project
|
||||||
|
pub fn update_project_tracker(instruction: Option<&str>, session_id: Option<&str>) -> Result<()> {
|
||||||
|
let current_dir = std::env::current_dir()?;
|
||||||
|
let mut tracker = ProjectTracker::load()?;
|
||||||
|
tracker.update_project(¤t_dir, instruction, session_id)
|
||||||
|
}
|
||||||
@@ -290,6 +290,22 @@ impl Session {
|
|||||||
// Persist messages with provider for automatic description generation
|
// Persist messages with provider for automatic description generation
|
||||||
session::persist_messages(&self.session_file, &self.messages, Some(provider)).await?;
|
session::persist_messages(&self.session_file, &self.messages, Some(provider)).await?;
|
||||||
|
|
||||||
|
// Track the current directory and last instruction in projects.json
|
||||||
|
let session_id = self
|
||||||
|
.session_file
|
||||||
|
.file_stem()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.map(|s| s.to_string());
|
||||||
|
|
||||||
|
if let Err(e) =
|
||||||
|
crate::project_tracker::update_project_tracker(Some(&message), session_id.as_deref())
|
||||||
|
{
|
||||||
|
eprintln!(
|
||||||
|
"Warning: Failed to update project tracker with instruction: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
self.process_agent_response(false).await?;
|
self.process_agent_response(false).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -356,6 +372,20 @@ impl Session {
|
|||||||
|
|
||||||
self.messages.push(Message::user().with_text(&content));
|
self.messages.push(Message::user().with_text(&content));
|
||||||
|
|
||||||
|
// Track the current directory and last instruction in projects.json
|
||||||
|
let session_id = self
|
||||||
|
.session_file
|
||||||
|
.file_stem()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.map(|s| s.to_string());
|
||||||
|
|
||||||
|
if let Err(e) = crate::project_tracker::update_project_tracker(
|
||||||
|
Some(&content),
|
||||||
|
session_id.as_deref(),
|
||||||
|
) {
|
||||||
|
eprintln!("Warning: Failed to update project tracker with instruction: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the provider from the agent for description generation
|
// Get the provider from the agent for description generation
|
||||||
let provider = self.agent.provider().await?;
|
let provider = self.agent.provider().await?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user