use crate::adapter::argv_to_unilang_tokens;
use crate::commands::dot_routine;
use unilang::data::{ CommandDefinition, ErrorCode };
use unilang::interpreter::{ ExecutionContext, Interpreter };
use unilang::parser::{ Parser, UnilangParserOptions };
use unilang::registry::CommandRegistry;
use unilang::semantic::SemanticAnalyzer;
pub( super ) fn exit_code_for( e : &unilang::error::Error ) -> i32
{
if let unilang::error::Error::Execution( ref data ) = e
{
match data.code
{
ErrorCode::InternalError | ErrorCode::CommandNotImplemented => 2,
_ => 1,
}
}
else
{
1
}
}
pub( super ) fn build_registry() -> CommandRegistry
{
let mut registry = CommandRegistry::new();
{
let def = CommandDefinition::former()
.name( "." )
.description( "Show help (alias for .help)" )
.arguments( vec![] )
.hidden_from_list( true )
.end();
registry
.register_with_routine( &def, Box::new( dot_routine ) )
.expect( "internal error: failed to register ." );
}
crate::register_commands( &mut registry );
registry
}
pub( super ) fn print_usage( binary : &str )
{
use cli_fmt::help::*;
fn entries_for
(
names : &[ &str ],
cmds : &std::collections::HashMap< String, CommandDefinition >,
) -> Vec< cli_fmt::help::CommandEntry >
{
names.iter()
.filter_map( |&name|
cmds.get( name ).map( |cmd|
cli_fmt::help::CommandEntry { name : name.to_string(), desc : cmd.description().to_string() }
)
)
.collect()
}
const ACCOUNT_MGMT : &[ &str ] = &[
".accounts", ".account.save", ".account.use", ".account.delete",
".account.limits", ".account.relogin", ".account.rotate",
".account.renewal", ".account.inspect",
];
const STATUS_INFO : &[ &str ] = &[
".credentials.status", ".token.status", ".paths", ".usage", ".model",
];
let registry = build_registry();
let cmds = registry.commands();
let in_ordered = |name : &str|
ACCOUNT_MGMT.iter().chain( STATUS_INFO.iter() ).any( |&n| n == name );
let mut acct_extra : Vec< cli_fmt::help::CommandEntry > = cmds.iter()
.filter( |( name, cmd )| !cmd.hidden_from_list() && !in_ordered( name ) && name.starts_with( ".account" ) )
.map( |( name, cmd )| cli_fmt::help::CommandEntry { name : name.clone(), desc : cmd.description().to_string() } )
.collect();
let mut info_extra : Vec< cli_fmt::help::CommandEntry > = cmds.iter()
.filter( |( name, cmd )| !cmd.hidden_from_list() && !in_ordered( name ) && !name.starts_with( ".account" ) )
.map( |( name, cmd )| cli_fmt::help::CommandEntry { name : name.clone(), desc : cmd.description().to_string() } )
.collect();
acct_extra.sort_by( |a, b| a.name.cmp( &b.name ) );
info_extra.sort_by( |a, b| a.name.cmp( &b.name ) );
let mut acct_entries = entries_for( ACCOUNT_MGMT, &cmds );
let mut info_entries = entries_for( STATUS_INFO, &cmds );
acct_entries.append( &mut acct_extra );
info_entries.append( &mut info_extra );
let mut data = CliHelpData::default();
data.binary = binary.to_string();
data.tagline = "Manage Claude Code account credentials and token state.".to_string();
data.groups = vec!
[
CommandGroup { name : "Account management".to_string(), entries : acct_entries },
CommandGroup { name : "Status & info".to_string(), entries : info_entries },
];
data.options = vec!
[
OptionEntry { name : "format::text|json".to_string(), desc : "Output format (default: text)".to_string() },
OptionEntry { name : "dry::bool".to_string(), desc : "Dry-run preview (no changes)".to_string() },
OptionEntry { name : "name::EMAIL".to_string(), desc : "Account name".to_string() },
];
data.examples = vec!
[
ExampleEntry { invocation : format!( "{binary} .accounts" ), desc : None },
ExampleEntry { invocation : format!( "{binary} .account.use alice@acme.com" ), desc : None },
ExampleEntry { invocation : format!( "{binary} .usage" ), desc : None },
ExampleEntry { invocation : format!( "{binary} .credentials.status" ), desc : None },
];
print!( "{}", CliHelpTemplate::new( CliHelpStyle::default(), data ).render() );
}
pub( super ) fn run( binary : &str, argv : &[ String ] )
{
let ( tokens, needs_help ) = match argv_to_unilang_tokens( argv )
{
Ok( r ) => r,
Err( e ) =>
{
eprintln!( "Error: {e}" );
eprintln!( "Run '{binary} .help' for usage." );
std::process::exit( 1 );
}
};
if needs_help
{
print_usage( binary );
return;
}
let registry = build_registry();
let parser = Parser::new( UnilangParserOptions::default() );
let instruction = match parser.parse_from_argv( &tokens )
{
Ok( i ) => i,
Err( e ) =>
{
eprintln!( "Error: {e}" );
std::process::exit( 1 );
}
};
let instructions = [ instruction ];
let analyzer = SemanticAnalyzer::new( &instructions, ®istry );
let commands = match analyzer.analyze()
{
Ok( cmds ) => cmds,
Err( e ) =>
{
eprintln!( "Error: {e}" );
std::process::exit( exit_code_for( &e ) );
}
};
let interpreter = Interpreter::new( &commands, ®istry );
let mut context = ExecutionContext::default();
match interpreter.run( &mut context )
{
Ok( outputs ) =>
{
for out in outputs
{
print!( "{}", out.content );
}
}
Err( e ) =>
{
eprintln!( "Error: {e}" );
std::process::exit( exit_code_for( &e ) );
}
}
}