Files
bbs-md/.planning/phases/01-safety-foundation/01-01-PLAN.md
T

7.5 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
01-safety-foundation 01 execute 1
Cargo.toml
src/config.rs
src/main.rs
true
CONF-01
SHEL-02
truths artifacts key_links
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
path provides contains
src/config.rs Config struct, load_config(), config path resolution, BBS error formatting deny_unknown_fields
path provides contains
Cargo.toml Dependencies: toml, serde, clap, signal-hook signal-hook
from to via pattern
src/main.rs src/config.rs load_config() call before terminal init load_config
from to via pattern
src/main.rs clap::Parser Cli::parse_from with stripped argv[0] 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.

<execution_context> @/Users/ruohki/.claude/get-shit-done/workflows/execute-plan.md @/Users/ruohki/.claude/get-shit-done/templates/summary.md </execution_context>

@.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<Config, ConfigError>:

    • 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<PathBuf> 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:

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

<success_criteria>

  • 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 </success_criteria>
After completion, create `.planning/phases/01-safety-foundation/01-01-SUMMARY.md`