use core::fmt::Write as FmtWrite;
use unilang::{ VerifiedCommand, ExecutionContext, OutputData, ErrorData, ErrorCode };
use super::storage::{ create_storage, resolve_path_parameter, load_project_for_param };
use super::projects::{ build_families, group_into_conversations };
#[ allow( clippy::too_many_lines ) ]
#[ allow( clippy::needless_pass_by_value ) ]
#[ inline ]
pub fn list_routine( cmd : VerifiedCommand, _ctx : ExecutionContext )
-> core::result::Result< OutputData, ErrorData >
{
let project_type = cmd.get_string( "type" ).unwrap_or( "all" );
let verbosity = cmd.get_integer( "verbosity" ).unwrap_or( 1 );
if !( 0..=5 ).contains( &verbosity )
{
return Err( ErrorData::new(
ErrorCode::InternalError,
format!( "Invalid verbosity: {verbosity}. Valid range: 0-5" ),
) );
}
let _ = verbosity;
if project_type == "conversation"
{
let proj_id = cmd.get_string( "project" )
.ok_or_else( || ErrorData::new(
ErrorCode::InternalError,
"project parameter required for listing conversations".to_string(),
) )?;
let storage = create_storage()?;
let project = load_project_for_param( &storage, proj_id )?;
let sessions = project.all_sessions()
.map_err( | e | ErrorData::new( ErrorCode::InternalError, format!( "Failed to load sessions: {e}" ) ) )?;
let families = build_families( sessions );
let conversations = group_into_conversations( families );
let count_mode = cmd.get_boolean( "count" ).unwrap_or( false );
if count_mode
{
return Ok( OutputData::new( format!( "{}", conversations.len() ), "text" ) );
}
let mut out = String::new();
for conv in &conversations
{
if let Some( s ) = conv.root_session()
{
writeln!( out, "{}", s.id() ).unwrap();
}
}
return Ok( OutputData::new( out, "text" ) );
}
let path_filter = cmd.get_string( "path" );
let agent_filter = cmd.get_boolean( "agent" );
let min_entries_filter = if let Some( n ) = cmd.get_integer( "min_entries" )
{
if n < 0
{
return Err
(
ErrorData::new
(
ErrorCode::InternalError,
format!( "Invalid min_entries: {n}. Must be non-negative" )
)
);
}
#[ allow( clippy::cast_sign_loss, clippy::cast_possible_truncation ) ]
Some( n as usize )
}
else
{
None
};
let session_id_filter = cmd.get_string( "session" );
let path_filter = if let Some( param ) = path_filter
{
match resolve_path_parameter( param )
{
Ok( resolved ) => Some( resolved ),
Err( e ) =>
{
return Err
(
ErrorData::new
(
ErrorCode::InternalError,
format!( "Failed to resolve path parameter '{}': {}", ¶m, e )
)
);
}
}
}
else
{
None
};
let has_session_filters = session_id_filter.is_some()
|| agent_filter.is_some()
|| min_entries_filter.is_some();
let show_sessions = has_session_filters || cmd.get_boolean( "show_sessions" ).unwrap_or( false );
let storage = create_storage()?;
let project_filter = claude_storage_core::ProjectFilter
{
path_substring : path_filter,
min_entries : None,
min_sessions : None,
};
let session_filter = claude_storage_core::SessionFilter
{
agent_only : agent_filter,
min_entries : min_entries_filter,
session_id_substring : session_id_filter.map( std::string::ToString::to_string ),
};
let mut projects = match project_type
{
"uuid" => storage.list_uuid_projects(),
"path" => storage.list_path_projects(),
"all" => storage.list_projects(),
_ => return Err
(
ErrorData::new
(
ErrorCode::InternalError,
format!( "Invalid type: {project_type}. Valid values: uuid, path, all" )
)
),
}
.map_err( | e | ErrorData::new( ErrorCode::InternalError, format!( "Failed to list projects: {e}" ) ) )?;
if !project_filter.is_default()
{
projects.retain( | project |
{
project.matches_filter( &project_filter ).unwrap_or( false )
});
}
let mut output = String::new();
let noun = if projects.len() == 1 { "project" } else { "projects" };
writeln!( output, "Found {} {noun}:\n", projects.len() ).unwrap();
for mut project in projects
{
let Ok( session_count ) = project.count_sessions() else { continue }; let noun = if session_count == 1 { "conversation" } else { "conversations" };
writeln!( output, "{:?} ({session_count} {noun})", project.id() ).unwrap();
if show_sessions
{
let sessions = if session_filter.is_default()
{
match project.sessions()
{
Ok( s ) => s,
Err( _ ) => continue, }
}
else
{
match project.sessions_filtered( &session_filter )
{
Ok( s ) => s,
Err( _ ) => continue, }
};
for session in sessions
{
writeln!( output, " - {}", session.id() ).unwrap();
}
}
}
Ok( OutputData::new( output, "text" ) )
}