--- phase: 01-safety-foundation plan: 01 type: execute wave: 1 depends_on: [] files_modified: - Cargo.toml - src/config.rs - src/main.rs autonomous: true requirements: - CONF-01 - SHEL-02 must_haves: truths: - "App reads vault_path and theme from bbs.toml when the file exists" - "App uses sensible defaults (vault=./vault/, theme=default) when bbs.toml is missing" - "App rejects unknown keys in bbs.toml with a BBS-themed error message" - "App shows a friendly error and exits when vault path does not exist" - "App accepts --config flag to specify an alternate config path" - "App detects login shell mode when argv[0] starts with a dash" - "App strips the leading dash from argv[0] before clap parses arguments" artifacts: - path: "src/config.rs" provides: "Config struct, load_config(), config path resolution, BBS error formatting" contains: "deny_unknown_fields" - path: "Cargo.toml" provides: "Dependencies: toml, serde, clap, signal-hook" contains: "signal-hook" key_links: - from: "src/main.rs" to: "src/config.rs" via: "load_config() call before terminal init" pattern: "load_config" - from: "src/main.rs" to: "clap::Parser" via: "Cli::parse_from with stripped argv[0]" pattern: "parse_from" --- Add all Phase 1 dependencies to Cargo.toml, implement config loading from bbs.toml with strict TOML parsing and BBS-themed error messages, implement CLI argument parsing with --config flag, and detect login shell mode from argv[0]. Wire the early startup path in main.rs (everything before terminal initialization). Purpose: Config and CLI must work before the terminal enters raw mode, so errors can be printed normally. Login shell detection determines whether 'q' to quit is suppressed later. Output: `src/config.rs` with Config struct and load_config(), updated `Cargo.toml` with all Phase 1 deps, and early startup wiring in `src/main.rs`. @/Users/ruohki/.claude/get-shit-done/workflows/execute-plan.md @/Users/ruohki/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/01-safety-foundation/01-RESEARCH.md Task 1: Add Phase 1 dependencies and implement config + CLI Cargo.toml, src/config.rs, src/main.rs **Cargo.toml** — Add all Phase 1 dependencies alongside the existing `ratatui = "0.30.0"`: ```toml signal-hook = "0.4.3" toml = "1.0.3" serde = { version = "1.0", features = ["derive"] } clap = { version = "4.5", features = ["derive"] } ``` **src/config.rs** — Create with: 1. `Config` struct with `#[derive(Deserialize, Debug)]` and `#[serde(deny_unknown_fields)]`: - `vault_path: PathBuf` with `#[serde(default = "default_vault_path")]` — defaults to `PathBuf::from("./vault")` - `theme: String` with `#[serde(default = "default_theme")]` — defaults to `"default".to_string()` - Implement `Default` for `Config` 2. `ConfigError` enum with variants: - `ReadError(std::io::Error)` — file exists but can't be read - `ParseError(toml::de::Error)` — TOML syntax or unknown fields - `VaultNotFound(PathBuf)` — vault_path directory doesn't exist 3. `load_config(path: &std::path::Path) -> Result`: - If file doesn't exist, return `Ok(Config::default())` - Read file, parse with `toml::from_str()` - Validate vault_path exists as a directory (resolve relative paths against the config file's parent directory, NOT the cwd — this is important because the config lives next to the binary) - Return `Err(ConfigError::VaultNotFound(...))` if vault dir missing 4. `resolve_config_path(cli_config: Option<&std::path::Path>) -> PathBuf`: - If CLI provided a path, use it - Otherwise, use `std::env::current_exe().parent() / "bbs.toml"` 5. `print_config_error(err: &ConfigError)`: - Format BBS-themed error messages to stderr. Examples: - ParseError: `"SYSTEM ERROR: Configuration file corrupted. SysOp intervention required.\nDetail: {toml error message}"` - VaultNotFound: `"SYSTEM ERROR: Vault directory not found at '{path}'. SysOp must verify vault_path in bbs.toml."` - ReadError: `"SYSTEM ERROR: Cannot read configuration file. Check file permissions."` - These print with `eprintln!()` since terminal is not yet in raw mode 6. `Cli` struct with clap derive: - `#[derive(clap::Parser, Debug)]` - `#[command(name = "bbs-md", about = "BBS-style markdown vault reader")]` - `config: Option` field with `#[arg(long = "config", short = 'c', value_name = "FILE")]` 7. `detect_login_shell() -> bool`: - Check `std::env::args_os().next()` — if it starts with '-', return true 8. `parse_cli() -> Cli`: - Collect `std::env::args_os()`, strip leading dash from first element if present - Call `Cli::parse_from(args)` with the cleaned args **src/main.rs** — Replace the hello world with the early startup path: ```rust mod config; fn main() { // 1. Detect login shell BEFORE stripping argv[0] let is_login_shell = config::detect_login_shell(); // 2. Parse CLI (strips dash from argv[0] internally) let cli = config::parse_cli(); // 3. Resolve config path and load config let config_path = config::resolve_config_path(cli.config.as_deref()); let app_config = match config::load_config(&config_path) { Ok(c) => c, Err(e) => { config::print_config_error(&e); std::process::exit(1); } }; // Phase 1 placeholder: print loaded config for verification // This will be replaced by terminal init + event loop in Plan 03 println!("Config loaded: {:?}", app_config); println!("Login shell: {}", is_login_shell); } ``` Declare `mod config;` in main.rs. The placeholder `println!` lines will be replaced in Plan 03 when the event loop is wired. Run `cargo build` from the project root — must compile without errors. Run `cargo run` — should print "Config loaded: Config { vault_path: ... }" with defaults (since no bbs.toml exists yet). Create a test bbs.toml next to the binary with `vault_path = "./vault"` and `theme = "retro"`, create a `./vault` directory, run the binary — config should load successfully. Create a bbs.toml with an unknown key like `bogus = true` — app should print BBS-themed error and exit with code 1. `cargo build` succeeds. Config loads from bbs.toml with strict parsing, defaults work when file is missing, unknown keys produce BBS-themed errors, missing vault directory produces a friendly error, --config flag overrides config path, and login shell detection works. 1. `cargo build` compiles without errors or warnings 2. Running without bbs.toml uses defaults (vault=./vault, theme=default) 3. Running with valid bbs.toml loads specified values 4. Unknown TOML keys produce BBS-themed error output 5. Nonexistent vault_path produces friendly error and exit(1) 6. `--config /path/to/custom.toml` overrides default config location 7. Login shell detection returns true when argv[0] starts with dash - Config struct deserializes bbs.toml with deny_unknown_fields - Missing config file gracefully falls back to defaults - All error messages use BBS-themed tone - CLI parsing works with and without login shell dash prefix - All Phase 1 dependencies are in Cargo.toml After completion, create `.planning/phases/01-safety-foundation/01-01-SUMMARY.md`