@@ -518,8 +518,6 @@ fn display_project_config_result(
518518fn print_configuration ( config : & Config ) {
519519 let purple = colors:: accent_primary ( ) ;
520520 let cyan = colors:: accent_secondary ( ) ;
521- let coral = colors:: accent_tertiary ( ) ;
522- let yellow = colors:: warning ( ) ;
523521 let green = colors:: success ( ) ;
524522 let dim = colors:: text_secondary ( ) ;
525523 let dim_sep = colors:: text_dim ( ) ;
@@ -539,6 +537,11 @@ fn print_configuration(config: &Config) {
539537 print_section_header ( "GLOBAL" ) ;
540538
541539 print_config_row ( "Provider" , & config. default_provider , cyan, true ) ;
540+
541+ // Theme
542+ let theme = crate :: theme:: current ( ) ;
543+ print_config_row ( "Theme" , & theme. meta . name , purple, false ) ;
544+
542545 print_config_row (
543546 "Gitmoji" ,
544547 if config. use_gitmoji {
@@ -549,79 +552,197 @@ fn print_configuration(config: &Config) {
549552 if config. use_gitmoji { green } else { dim } ,
550553 false ,
551554 ) ;
552- print_config_row ( "Preset" , & config. instruction_preset , yellow , false ) ;
555+ print_config_row ( "Preset" , & config. instruction_preset , dim , false ) ;
553556 print_config_row (
554- "Subagent Timeout" ,
557+ "Timeout" ,
555558 & format ! ( "{}s" , config. subagent_timeout_secs) ,
556- coral ,
559+ dim ,
557560 false ,
558561 ) ;
559562
563+ // Config file paths
564+ if let Ok ( config_path) = Config :: get_personal_config_path ( ) {
565+ let home = dirs:: home_dir ( )
566+ . map ( |h| h. to_string_lossy ( ) . to_string ( ) )
567+ . unwrap_or_default ( ) ;
568+ let path_str = config_path. to_string_lossy ( ) . to_string ( ) ;
569+ let path_display = if home. is_empty ( ) {
570+ path_str
571+ } else {
572+ path_str. replace ( & home, "~" )
573+ } ;
574+ print_config_row ( "Config" , & path_display, dim, false ) ;
575+ }
576+
577+ // Project config status
578+ if let Ok ( project_path) = Config :: get_project_config_path ( )
579+ && project_path. exists ( )
580+ {
581+ print_config_row ( "Project" , ".irisconfig ✓" , green, false ) ;
582+ }
583+
560584 // Custom Instructions (if any)
561585 if !config. instructions . is_empty ( ) {
562586 println ! ( ) ;
563587 print_section_header ( "INSTRUCTIONS" ) ;
564- for line in config. instructions . lines ( ) {
565- println ! ( " {}" , line. truecolor( dim. 0 , dim. 1 , dim. 2 ) . italic( ) ) ;
588+ // Truncate long instructions for display
589+ let preview: String = config. instructions . lines ( ) . take ( 3 ) . collect :: < Vec < _ > > ( ) . join ( "\n " ) ;
590+ for line in preview. lines ( ) {
591+ println ! ( " {}" , line. truecolor( dim. 0 , dim. 1 , dim. 2 ) . italic( ) ) ;
592+ }
593+ let total_lines = config. instructions . lines ( ) . count ( ) ;
594+ if total_lines > 3 {
595+ println ! (
596+ " {}" ,
597+ format!( "… ({} more lines)" , total_lines - 3 )
598+ . truecolor( dim_sep. 0 , dim_sep. 1 , dim_sep. 2 )
599+ . italic( )
600+ ) ;
566601 }
567602 }
568603
569- // Show all configured providers
570- // For personal configs: show only those with API keys
571- // For project configs: show all providers (they never have API keys)
572- let mut providers: Vec < _ > = config
573- . providers
574- . iter ( )
575- . filter ( |( _, cfg) | config. is_project_config || !cfg. api_key . is_empty ( ) )
576- . collect ( ) ;
577- providers. sort_by_key ( |( name, _) | name. as_str ( ) ) ;
604+ // Show ALL providers — active provider first, then rest alphabetically
605+ let mut provider_names: Vec < String > = Provider :: ALL . iter ( ) . map ( |p| p. name ( ) . to_string ( ) ) . collect ( ) ;
606+ provider_names. sort ( ) ;
607+ // Move active provider to front
608+ if let Some ( pos) = provider_names. iter ( ) . position ( |n| n == & config. default_provider ) {
609+ let active = provider_names. remove ( pos) ;
610+ provider_names. insert ( 0 , active) ;
611+ }
578612
579- for ( provider_name, provider_config ) in providers {
613+ for provider_name in & provider_names {
580614 println ! ( ) ;
581- let is_active = provider_name == & config. default_provider ;
582- let header = if is_active {
583- format ! ( "{} ✦" , provider_name. to_uppercase( ) )
584- } else {
585- provider_name. to_uppercase ( )
586- } ;
587- print_section_header ( & header) ;
615+ print_provider_section ( config, provider_name) ;
616+ }
617+
618+ println ! ( ) ;
619+ println ! (
620+ "{}" ,
621+ "─" . repeat( 44 ) . truecolor( dim_sep. 0 , dim_sep. 1 , dim_sep. 2 )
622+ ) ;
623+ println ! ( ) ;
624+ }
588625
589- // Model
590- print_config_row ( "Model" , & provider_config. model , cyan, true ) ;
626+ /// Print a single provider section with all its details
627+ fn print_provider_section ( config : & Config , provider_name : & str ) {
628+ let cyan = colors:: accent_secondary ( ) ;
629+ let coral = colors:: accent_tertiary ( ) ;
630+ let yellow = colors:: warning ( ) ;
631+ let green = colors:: success ( ) ;
632+ let dim = colors:: text_secondary ( ) ;
633+ let error_red: ( u8 , u8 , u8 ) = ( 255 , 99 , 99 ) ;
591634
592- // Fast Model
593- let fast_model = provider_config. fast_model . as_deref ( ) . unwrap_or ( "(default)" ) ;
594- print_config_row ( "Fast Model" , fast_model, cyan, false ) ;
635+ let is_active = provider_name == config. default_provider ;
636+ let provider: Option < Provider > = provider_name. parse ( ) . ok ( ) ;
595637
596- // Token Limit
597- if let Some ( limit) = provider_config. token_limit {
598- print_config_row ( "Token Limit" , & limit. to_string ( ) , coral, false ) ;
638+ let header = if is_active {
639+ format ! ( "{} ✦" , provider_name. to_uppercase( ) )
640+ } else {
641+ provider_name. to_uppercase ( )
642+ } ;
643+ print_section_header ( & header) ;
644+
645+ let provider_config = config. providers . get ( provider_name) ;
646+
647+ // Model (resolve effective model)
648+ let model = provider_config
649+ . and_then ( |pc| provider. map ( |p| pc. effective_model ( p) . to_string ( ) ) )
650+ . or_else ( || provider. map ( |p| p. default_model ( ) . to_string ( ) ) )
651+ . unwrap_or_default ( ) ;
652+ print_config_row ( "Model" , & model, cyan, is_active) ;
653+
654+ // Fast Model (resolve effective)
655+ let fast_model = provider_config
656+ . and_then ( |pc| provider. map ( |p| pc. effective_fast_model ( p) . to_string ( ) ) )
657+ . or_else ( || provider. map ( |p| p. default_fast_model ( ) . to_string ( ) ) )
658+ . unwrap_or_default ( ) ;
659+ print_config_row ( "Fast Model" , & fast_model, dim, false ) ;
660+
661+ // Context window
662+ if let Some ( p) = provider {
663+ let effective_limit = provider_config
664+ . map_or_else ( || p. context_window ( ) , |pc| pc. effective_token_limit ( p) ) ;
665+ let limit_str = format_token_count ( effective_limit) ;
666+ let is_custom = provider_config. and_then ( |pc| pc. token_limit ) . is_some ( ) ;
667+ if is_custom {
668+ print_config_row ( "Context" , & format ! ( "{limit_str} (custom)" ) , coral, false ) ;
669+ } else {
670+ print_config_row ( "Context" , & limit_str, dim, false ) ;
599671 }
672+ }
673+
674+ // API Key status
675+ if let Some ( p) = provider {
676+ let has_config_key = provider_config. is_some_and ( ProviderConfig :: has_api_key) ;
677+ let has_env_key = std:: env:: var ( p. api_key_env ( ) ) . is_ok ( ) ;
678+ let env_var = p. api_key_env ( ) ;
600679
601- // Additional Parameters
602- if !provider_config. additional_params . is_empty ( ) {
680+ let ( status, status_color) = if has_config_key {
681+ // Safe: has_config_key guarantees provider_config is Some with a key
682+ let key = & provider_config. expect ( "checked above" ) . api_key ;
683+ let masked = mask_api_key ( key) ;
684+ ( format ! ( "✓ {masked}" ) , green)
685+ } else if has_env_key {
686+ ( format ! ( "✓ ${env_var}" ) , green)
687+ } else {
688+ ( format ! ( "✗ not set → ${env_var}" ) , error_red)
689+ } ;
690+ print_config_row ( "API Key" , & status, status_color, false ) ;
691+
692+ // Show format warning if key exists but has bad format
693+ let key_value = if has_config_key {
694+ provider_config. map ( |pc| pc. api_key . clone ( ) )
695+ } else if has_env_key {
696+ std:: env:: var ( p. api_key_env ( ) ) . ok ( )
697+ } else {
698+ None
699+ } ;
700+ if let Some ( ref key) = key_value
701+ && let Err ( warning) = p. validate_api_key_format ( key)
702+ {
603703 println ! (
604- " {} {}" ,
605- "Params" . truecolor( dim. 0 , dim. 1 , dim. 2 ) ,
606- "─" . truecolor( dim_sep. 0 , dim_sep. 1 , dim_sep. 2 )
704+ " {}" ,
705+ format!( "⚠ {warning}" ) . truecolor( yellow. 0 , yellow. 1 , yellow. 2 )
607706 ) ;
608- for ( key, value) in & provider_config. additional_params {
609- println ! (
610- " {} {} {}" ,
611- key. truecolor( cyan. 0 , cyan. 1 , cyan. 2 ) ,
612- "→" . truecolor( dim_sep. 0 , dim_sep. 1 , dim_sep. 2 ) ,
613- value. truecolor( dim. 0 , dim. 1 , dim. 2 )
614- ) ;
615- }
616707 }
617708 }
618709
619- println ! ( ) ;
620- println ! (
621- "{}" ,
622- "─" . repeat( 40 ) . truecolor( dim_sep. 0 , dim_sep. 1 , dim_sep. 2 )
623- ) ;
624- println ! ( ) ;
710+ // Additional Parameters
711+ if let Some ( pc) = provider_config
712+ && !pc. additional_params . is_empty ( )
713+ {
714+ for ( key, value) in & pc. additional_params {
715+ print_config_row ( key, value, dim, false ) ;
716+ }
717+ }
718+ }
719+
720+ /// Format a token count in human-readable form (128K, 200K, 1M)
721+ fn format_token_count ( count : usize ) -> String {
722+ if count >= 1_000_000 && count. is_multiple_of ( 1_000_000 ) {
723+ format ! ( "{}M tokens" , count / 1_000_000 )
724+ } else if count >= 1_000 {
725+ format ! ( "{}K tokens" , count / 1_000 )
726+ } else {
727+ format ! ( "{count} tokens" )
728+ }
729+ }
730+
731+ /// Mask an API key for display, showing only prefix and last 4 chars
732+ fn mask_api_key ( key : & str ) -> String {
733+ if key. len ( ) <= 8 {
734+ return "••••" . to_string ( ) ;
735+ }
736+ // Show the prefix (e.g. "sk-ant-") and last 4 chars
737+ let prefix_end = key. find ( '-' ) . map_or ( 4 , |i| {
738+ // Find the last hyphen in the prefix portion (first 12 chars)
739+ key[ ..12 . min ( key. len ( ) ) ]
740+ . rfind ( '-' )
741+ . map_or ( i + 1 , |j| j + 1 )
742+ } ) ;
743+ let prefix = & key[ ..prefix_end. min ( key. len ( ) ) ] ;
744+ let suffix = & key[ key. len ( ) - 4 ..] ;
745+ format ! ( "{prefix}••••{suffix}" )
625746}
626747
627748/// Print a section header in `SilkCircuit` style
0 commit comments