|
|
""" |
|
|
Interactive shell for the Folio CLI. |
|
|
|
|
|
This module provides the main entry point for the Folio CLI interactive shell. |
|
|
""" |
|
|
|
|
|
import argparse |
|
|
import os |
|
|
import traceback |
|
|
|
|
|
from prompt_toolkit import PromptSession |
|
|
from prompt_toolkit.completion import NestedCompleter |
|
|
from prompt_toolkit.history import FileHistory |
|
|
from prompt_toolkit.shortcuts import confirm |
|
|
from rich.console import Console |
|
|
|
|
|
from src.focli.commands import execute_command, get_command_registry |
|
|
from src.focli.commands.simulate import simulate_command |
|
|
from src.focli.utils import load_portfolio |
|
|
|
|
|
|
|
|
def create_completer(): |
|
|
"""Create a nested completer for command auto-completion. |
|
|
|
|
|
Returns: |
|
|
NestedCompleter for command auto-completion |
|
|
""" |
|
|
|
|
|
commands = get_command_registry() |
|
|
|
|
|
|
|
|
completion_dict = {} |
|
|
for cmd_name, cmd_info in commands.items(): |
|
|
if cmd_info.get("subcommands"): |
|
|
completion_dict[cmd_name] = { |
|
|
subcmd: None for subcmd in cmd_info["subcommands"] |
|
|
} |
|
|
else: |
|
|
completion_dict[cmd_name] = None |
|
|
|
|
|
return NestedCompleter.from_nested_dict(completion_dict) |
|
|
|
|
|
|
|
|
def initialize_state(): |
|
|
"""Initialize the application state. |
|
|
|
|
|
Returns: |
|
|
Dictionary containing the initial application state |
|
|
""" |
|
|
return { |
|
|
"portfolio_groups": None, |
|
|
"portfolio_summary": None, |
|
|
"loaded_portfolio": None, |
|
|
"last_simulation": None, |
|
|
"simulation_history": [], |
|
|
"last_position": None, |
|
|
"position_simulations": {}, |
|
|
"filtered_groups": None, |
|
|
"simulation_presets": { |
|
|
"default": {"range": 20.0, "steps": 13}, |
|
|
"detailed": {"range": 20.0, "steps": 21, "detailed": True}, |
|
|
"quick": {"range": 10.0, "steps": 5}, |
|
|
}, |
|
|
"command_history": [], |
|
|
} |
|
|
|
|
|
|
|
|
def load_default_portfolio(state, console): |
|
|
"""Try to load the default portfolio. |
|
|
|
|
|
Args: |
|
|
state: Application state |
|
|
console: Rich console for output |
|
|
|
|
|
Returns: |
|
|
True if portfolio was loaded successfully, False otherwise |
|
|
""" |
|
|
default_portfolio = "private-data/portfolio-private.csv" |
|
|
try: |
|
|
load_portfolio(default_portfolio, state, console) |
|
|
return True |
|
|
except Exception as e: |
|
|
console.print(f"[yellow]Could not load default portfolio: {e}[/yellow]") |
|
|
console.print( |
|
|
"[yellow]Use 'portfolio load <path>' to load a portfolio.[/yellow]" |
|
|
) |
|
|
return False |
|
|
|
|
|
|
|
|
def main(): |
|
|
"""Main entry point for the Folio CLI.""" |
|
|
|
|
|
parser = argparse.ArgumentParser(description="Folio CLI") |
|
|
parser.add_argument( |
|
|
"--simulate", action="store_true", help="Run simulation directly" |
|
|
) |
|
|
parser.add_argument( |
|
|
"--preset", type=str, help="Simulation preset to use (default, quick, detailed)" |
|
|
) |
|
|
args = parser.parse_args() |
|
|
|
|
|
console = Console() |
|
|
|
|
|
|
|
|
state = initialize_state() |
|
|
|
|
|
|
|
|
if args.simulate: |
|
|
console.print("[bold cyan]Folio CLI - Direct Simulation[/bold cyan]") |
|
|
|
|
|
|
|
|
if load_default_portfolio(state, console): |
|
|
|
|
|
sim_args = [] |
|
|
if args.preset: |
|
|
sim_args = ["-p", args.preset] |
|
|
|
|
|
|
|
|
simulate_command(sim_args, state, console) |
|
|
return |
|
|
else: |
|
|
console.print( |
|
|
"[bold red]Error:[/bold red] Cannot run simulation without a portfolio." |
|
|
) |
|
|
console.print( |
|
|
"Please run the CLI without --simulate to load a portfolio first." |
|
|
) |
|
|
return |
|
|
|
|
|
|
|
|
console.print("[bold cyan]Folio Interactive Shell[/bold cyan]") |
|
|
console.print("Type 'help' for available commands.") |
|
|
|
|
|
|
|
|
history_file = os.path.expanduser("~/.folio_history") |
|
|
|
|
|
|
|
|
session = PromptSession( |
|
|
completer=create_completer(), history=FileHistory(history_file) |
|
|
) |
|
|
|
|
|
|
|
|
load_default_portfolio(state, console) |
|
|
|
|
|
|
|
|
while True: |
|
|
try: |
|
|
|
|
|
text = session.prompt("folio> ") |
|
|
|
|
|
if not text.strip(): |
|
|
continue |
|
|
|
|
|
|
|
|
if text.strip().lower() == "exit": |
|
|
if confirm_exit(): |
|
|
break |
|
|
continue |
|
|
|
|
|
|
|
|
execute_command(text, state, console) |
|
|
|
|
|
|
|
|
state["command_history"].append(text) |
|
|
|
|
|
except KeyboardInterrupt: |
|
|
|
|
|
console.print("[yellow]Use 'exit' to exit the application.[/yellow]") |
|
|
except EOFError: |
|
|
|
|
|
break |
|
|
except Exception as e: |
|
|
|
|
|
console.print(f"[bold red]Error:[/bold red] {e}") |
|
|
console.print(traceback.format_exc()) |
|
|
|
|
|
|
|
|
def confirm_exit(): |
|
|
"""Confirm exit with the user. |
|
|
|
|
|
Returns: |
|
|
True if the user confirms, False otherwise |
|
|
""" |
|
|
return confirm("Are you sure you want to exit?") |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
main() |
|
|
|