diff --git a/bot/vikingbot/cli/commands.py b/bot/vikingbot/cli/commands.py index 9256fb4b..76d923f0 100644 --- a/bot/vikingbot/cli/commands.py +++ b/bot/vikingbot/cli/commands.py @@ -25,7 +25,7 @@ from vikingbot.agent.loop import AgentLoop from vikingbot.bus.queue import MessageBus from vikingbot.channels.manager import ChannelManager -from vikingbot.config.loader import ensure_config, get_config_path, get_data_dir, load_config +from vikingbot.config.loader import ensure_config, get_config_path, get_data_dir, load_config, save_init_config from vikingbot.config.schema import SessionKey from vikingbot.cron.service import CronService from vikingbot.cron.types import CronJob @@ -194,6 +194,30 @@ def main( """vikingbot - Personal AI Assistant.""" pass +@app.command() +def onboard(): + """Initialize vikingbot configuration and workspace.""" + from vikingbot.config.loader import get_config_path, save_config + from vikingbot.config.schema import Config + + config_path = get_config_path() + + if config_path.exists(): + console.print(f"[yellow]Config already exists at {config_path}[/yellow]") + if not typer.confirm("Overwrite?"): + raise typer.Exit() + + # Create default config + config = Config() + save_init_config(config) + console.print(f"[green]✓[/green] Created config at {config_path}") + + console.print(f"\n{__logo__} vikingbot is ready!") + console.print("\nNext steps:") + console.print(" 1. Add your API key to [cyan]~/.openviking/ov.conf[/cyan]") + console.print(" Get one at: https://openrouter.ai/keys") + console.print(" 2. Chat: [cyan]vikingbot agent -m \"Hello!\"[/cyan]") + def _make_provider(config, langfuse_client: None = None): """Create LiteLLMProvider from config. Allows starting without API key.""" diff --git a/bot/vikingbot/config/loader.py b/bot/vikingbot/config/loader.py index d32b7438..1a24df8a 100644 --- a/bot/vikingbot/config/loader.py +++ b/bot/vikingbot/config/loader.py @@ -177,6 +177,24 @@ def save_config(config: Config, config_path: Path | None = None) -> None: with open(path, "w") as f: json.dump(full_data, f, indent=2) +def save_init_config(config: Config, config_path: Path | None = None) -> None: + """ + Save configuration to file. + + Args: + config: Configuration to save. + config_path: Optional path to save to. Uses default if not provided. + """ + path = config_path or get_config_path() + path.parent.mkdir(parents=True, exist_ok=True) + + # Convert to camelCase format + data = config.model_dump() + data = convert_to_camel(data) + + with open(path, "w") as f: + json.dump(data, f, indent=2) + def convert_keys(data: Any) -> Any: """Convert camelCase keys to snake_case for Pydantic."""