@@ -15,6 +15,13 @@ namespace CodePunk.Console.Commands;
1515
1616internal static class RootCommandFactory
1717{
18+ private const int MaxTitleLength = 80 ;
19+ private static string TrimTitle ( string title )
20+ {
21+ if ( string . IsNullOrWhiteSpace ( title ) ) return title ;
22+ var trimmed = title . Trim ( ) ;
23+ return trimmed . Length > MaxTitleLength ? trimmed [ ..MaxTitleLength ] : trimmed ;
24+ }
1825 public static RootCommand Create ( IServiceProvider services )
1926 {
2027 var root = new RootCommand ( "CodePunk CLI" )
@@ -79,13 +86,14 @@ private static Command BuildRun(IServiceProvider services)
7986 }
8087 }
8188 services . GetRequiredService < InteractiveChatSession > ( ) . UpdateDefaults ( providerOverride , modelOverride ) ;
89+ var resolvedModelForStore = modelOverride ?? ( string . IsNullOrWhiteSpace ( model ) ? null : model ) ;
8290 if ( string . IsNullOrWhiteSpace ( sessionId ) )
8391 {
84- sessionId = await sessionStore . CreateAsync ( TrimTitle ( message ) , agent , model ) ;
92+ sessionId = await sessionStore . CreateAsync ( TrimTitle ( message ) , agent , resolvedModelForStore ) ;
8593 }
8694 else if ( await sessionStore . GetAsync ( sessionId ) == null )
8795 {
88- sessionId = await sessionStore . CreateAsync ( TrimTitle ( message ) , agent , model ) ;
96+ sessionId = await sessionStore . CreateAsync ( TrimTitle ( message ) , agent , resolvedModelForStore ) ;
8997 }
9098 await sessionStore . AppendMessageAsync ( sessionId , "user" , message ) ;
9199 var response = await chatLoop . RunSingleAsync ( message ) ;
@@ -239,17 +247,23 @@ private static Command BuildAgent(IServiceProvider services)
239247 private static Command BuildModels ( IServiceProvider services )
240248 {
241249 var jsonOpt = new Option < bool > ( "--json" , "Output JSON" ) ;
242- var cmd = new Command ( "models" , "List available models from configured providers" ) { jsonOpt } ;
250+ var availableOnlyOpt = new Option < bool > ( "--available-only" , "Show only providers with stored API keys" ) ;
251+ var cmd = new Command ( "models" , "List available models from configured providers" ) { jsonOpt , availableOnlyOpt } ;
243252 cmd . SetHandler ( async ( InvocationContext ctx ) =>
244253 {
245254 try
246255 {
247256 var json = ctx . ParseResult . GetValueForOption ( jsonOpt ) ;
257+ var availableOnly = ctx . ParseResult . GetValueForOption ( availableOnlyOpt ) ;
248258 var llm = services . GetRequiredService < ILLMService > ( ) ;
249259 var providers = llm . GetProviders ( ) ?? Array . Empty < ILLMProvider > ( ) ;
250- var rows = new List < ( string Provider , string Id , string Name , int Context , int MaxTokens , bool Tools , bool Streaming ) > ( ) ;
260+ var authStore = services . GetRequiredService < IAuthStore > ( ) ;
261+ var authenticated = ( await authStore . LoadAsync ( ) ) . Keys . ToHashSet ( StringComparer . OrdinalIgnoreCase ) ;
262+ var rows = new List < ( string Provider , string Id , string Name , int Context , int MaxTokens , bool Tools , bool Streaming , bool HasKey ) > ( ) ;
251263 foreach ( var p in providers . OrderBy ( p => p . Name , StringComparer . OrdinalIgnoreCase ) )
252264 {
265+ var hasKey = authenticated . Contains ( p . Name ) || authenticated . Contains ( p . Name . Replace ( "Provider" , "" , StringComparison . OrdinalIgnoreCase ) ) ;
266+ if ( availableOnly && ! hasKey ) continue ;
253267 IReadOnlyList < CodePunk . Core . Abstractions . LLMModel > remote = Array . Empty < CodePunk . Core . Abstractions . LLMModel > ( ) ;
254268 try
255269 {
@@ -259,12 +273,12 @@ private static Command BuildModels(IServiceProvider services)
259273
260274 var models = ( remote != null && remote . Count > 0 ) ? remote : p . Models ;
261275 foreach ( var m in models . OrderBy ( m => m . Id , StringComparer . OrdinalIgnoreCase ) )
262- rows . Add ( ( p . Name , m . Id , m . Name , m . ContextWindow , m . MaxTokens , m . SupportsTools , m . SupportsStreaming ) ) ;
276+ rows . Add ( ( p . Name , m . Id , m . Name , m . ContextWindow , m . MaxTokens , m . SupportsTools , m . SupportsStreaming , hasKey ) ) ;
263277 }
264278 var writer = ctx . Console . Out ;
265279 if ( json )
266280 {
267- var jsonOut = System . Text . Json . JsonSerializer . Serialize ( rows . Select ( r => new { provider = r . Provider , id = r . Id , name = r . Name , context = r . Context , maxTokens = r . MaxTokens , tools = r . Tools , streaming = r . Streaming } ) , new System . Text . Json . JsonSerializerOptions { WriteIndented = true } ) ;
281+ var jsonOut = System . Text . Json . JsonSerializer . Serialize ( rows . Select ( r => new { provider = r . Provider , id = r . Id , name = r . Name , context = r . Context , maxTokens = r . MaxTokens , tools = r . Tools , streaming = r . Streaming , hasKey = r . HasKey } ) , new System . Text . Json . JsonSerializerOptions { WriteIndented = true } ) ;
268282 writer . Write ( jsonOut + "\n " ) ;
269283 return ;
270284 }
@@ -290,9 +304,11 @@ private static Command BuildModels(IServiceProvider services)
290304 table . AddColumn ( new TableColumn ( "Max" ) . Centered ( ) ) ;
291305 table . AddColumn ( new TableColumn ( "Tools" ) . Centered ( ) ) ;
292306 table . AddColumn ( new TableColumn ( "Stream" ) . Centered ( ) ) ;
307+ table . AddColumn ( new TableColumn ( "Key" ) . Centered ( ) ) ;
293308 foreach ( var r in rows )
294309 {
295- table . AddRow ( ConsoleStyles . Accent ( r . Provider ) , r . Id , r . Name , r . Context . ToString ( ) , r . MaxTokens . ToString ( ) , r . Tools ? "[green]✓[/]" : "[grey]-[/]" , r . Streaming ? "[green]✓[/]" : "[grey]-[/]" ) ;
310+ var providerLabel = r . HasKey ? ConsoleStyles . Accent ( r . Provider ) : $ "[grey]{ r . Provider } [/]";
311+ table . AddRow ( providerLabel , r . Id , r . Name , r . Context . ToString ( ) , r . MaxTokens . ToString ( ) , r . Tools ? "[green]✓[/]" : "[grey]-[/]" , r . Streaming ? "[green]✓[/]" : "[grey]-[/]" , r . HasKey ? "[green]✓[/]" : "[red]✗[/]" ) ;
296312 writer . Write ( $ "{ r . Provider } \t { r . Id } \t { r . Name } \n ") ;
297313 }
298314 console ? . Write ( table ) ;
@@ -356,9 +372,4 @@ internal static Command CreateModelsCommandForTests(IServiceProvider services)
356372 return cmd ;
357373 }
358374
359- private static string TrimTitle ( string input )
360- {
361- var oneLine = input . Replace ( "\n " , " " ) . Trim ( ) ;
362- return oneLine . Length <= 60 ? oneLine : oneLine [ ..60 ] ;
363- }
364375}
0 commit comments