mps-rs 1.8.2

MPS — plain-text personal productivity CLI (Rust)
Documentation
use crate::config::Config;
use anyhow::Result;
use colored::Colorize;
use std::io::{self, BufRead, Write};
use std::path::{Path, PathBuf};

pub fn run(config_path: &Path, force: bool) -> Result<()> {
    if config_path.exists() && !force {
        println!("{}", "Config file already exists:".yellow());
        println!("  {}", config_path.display());
        println!();
        print!("Overwrite it? [y/N] ");
        io::stdout().flush()?;
        let mut line = String::new();
        io::stdin().lock().read_line(&mut line)?;
        let answer = line.trim().to_lowercase();
        if answer != "y" && answer != "yes" {
            println!("Aborted.");
            println!(
                "  {} use {} to materialise all config keys into the existing file",
                "tip:".cyan(),
                "mps config init".bold()
            );
            return Ok(());
        }
    }

    println!("{}", "mps init — interactive setup".white().bold());
    println!("  Press Enter to accept the default shown in [brackets].");
    println!();

    let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
    let default_mps_dir = home.join(".mps");

    let mps_dir_str = prompt(
        "Journal directory (mps_dir)",
        &default_mps_dir.display().to_string(),
    )?;
    let mps_dir = PathBuf::from(&mps_dir_str);

    let default_storage_dir = mps_dir.join("mps");
    let storage_dir_str = prompt(
        "Storage directory (storage_dir)",
        &default_storage_dir.display().to_string(),
    )?;
    let storage_dir = PathBuf::from(&storage_dir_str);

    let log_file = mps_dir.join("mps.log");

    let git_remote = prompt("Git remote", "origin")?;
    let git_branch = prompt("Git branch", "master")?;

    println!();
    println!("  Default command when 'mps' is run with no arguments:");
    println!("    open  — open today's file in $EDITOR");
    println!("    list  — list today's elements");
    let cmd_input = prompt("Default command (open/list)", "open")?;
    let default_command = if cmd_input.trim().to_lowercase().starts_with('l') {
        "list"
    } else {
        "open"
    };

    println!();
    println!(
        "{}",
        "── LLM chat settings ─────────────────────────────────".white()
    );
    println!("  Leave URL blank to auto-detect Ollama (:11434) or llama.cpp (:8080).");
    let chat_url_input = prompt("Chat LLM URL (blank = auto-detect)", "")?;
    let chat_url = if chat_url_input.trim().is_empty() {
        None
    } else {
        Some(chat_url_input.trim().to_string())
    };
    let chat_model = prompt("Chat model name", "llama3.2")?;

    println!();

    let mut cfg = Config::default_config()?;
    cfg.mps_dir = mps_dir;
    cfg.storage_dir = storage_dir;
    cfg.log_file = log_file;
    cfg.git_remote = git_remote;
    cfg.git_branch = git_branch;
    cfg.default_command = default_command.to_string();
    cfg.chat.url = chat_url;
    cfg.chat.model = chat_model;

    if let Some(parent) = config_path.parent() {
        if !parent.as_os_str().is_empty() {
            std::fs::create_dir_all(parent)?;
        }
    }
    cfg.save(config_path)?;
    cfg.ensure_dirs()?;

    println!("{} {}", "created:".green(), config_path.display());
    println!("{} {}", "created:".green(), cfg.mps_dir.display());
    println!("{} {}", "created:".green(), cfg.storage_dir.display());
    println!();
    println!(
        "  {} run {} to see all settings",
        "tip:".cyan(),
        "mps config show".bold()
    );
    println!(
        "  {} run {} to validate config health",
        "tip:".cyan(),
        "mps config check".bold()
    );
    println!(
        "  {} run {} to push config to git",
        "tip:".cyan(),
        "mps autogit".bold()
    );

    Ok(())
}

fn prompt(label: &str, default: &str) -> Result<String> {
    if default.is_empty() {
        print!("  {}: ", label);
    } else {
        print!("  {} [{}]: ", label, default);
    }
    io::stdout().flush()?;
    let mut line = String::new();
    io::stdin().lock().read_line(&mut line)?;
    let trimmed = line.trim().to_string();
    if trimmed.is_empty() && !default.is_empty() {
        Ok(default.to_string())
    } else {
        Ok(trimmed)
    }
}