devist 0.26.0

Project bootstrap CLI for AI-assisted development. Spin up new projects from templates, manage backends, and keep your codebase comprehensible.
//! Compile-in tech wisdom — domain knowledge that doesn't depend on
//! the user's history. Auto-injected into the advice prompt when the
//! project's detected tech tags match.
//!
//! Curated by hand for high signal-to-noise. Add cards here as
//! patterns prove themselves across multiple projects. The bar is the
//! same as for Reso strong memories: ecosystem-specific facts a
//! thoughtful senior would consistently apply, that aren't obvious
//! from reading any single file.

use std::path::Path;

#[derive(Debug, Clone, Copy)]
pub struct TechCard {
    pub tag: &'static str,
    pub priority: &'static str, // "constraint" | "strong"
    pub text: &'static str,
}

pub const TECH_CARDS: &[TechCard] = &[
    // ---- React ----
    TechCard {
        tag: "react",
        priority: "strong",
        text: "useEffect with an empty deps array runs once at mount. If it subscribes, sets a timer, opens a connection, or otherwise allocates, it MUST return a cleanup function to avoid leaks on unmount.",
    },
    TechCard {
        tag: "react",
        priority: "constraint",
        text: "Never call setState inside the render body. Always inside event handlers, effects, or callbacks. Calling during render causes infinite re-render loops.",
    },
    // ---- Rust ----
    TechCard {
        tag: "rust",
        priority: "strong",
        text: "For distributable binaries, prefer rustls over native-tls/openssl. Avoids requiring a system OpenSSL at runtime; cross-compilation is simpler.",
    },
    TechCard {
        tag: "rust",
        priority: "strong",
        text: "Binaries can use Box<dyn Error> or anyhow::Error freely. Libraries should expose typed errors (thiserror) so downstream code can match cases.",
    },
    // ---- Supabase ----
    TechCard {
        tag: "supabase",
        priority: "constraint",
        text: "RLS policies must be tested with the anon role explicitly. service_role bypasses RLS entirely and gives misleading 'works for me' results during dev.",
    },
    TechCard {
        tag: "supabase",
        priority: "constraint",
        text: "Never put service_role keys in client-side code or env vars exposed to the browser. They bypass all RLS and grant full DB access.",
    },
];

/// Detect tech tags from the project directory by looking at well-
/// known marker files. Cheap (no recursive walk; checks roots only).
pub fn detect_tech(project_dir: &Path) -> Vec<String> {
    let mut tags = Vec::new();
    if project_dir.join("Cargo.toml").exists() {
        tags.push("rust".into());
    }
    if project_dir.join("package.json").exists() {
        if let Ok(s) = std::fs::read_to_string(project_dir.join("package.json")) {
            // Cheap substring check — avoid pulling in serde_json for a tag detector.
            if s.contains("\"react\"") || s.contains("\"next\"") {
                tags.push("react".into());
            }
        }
    }
    if project_dir.join("supabase").is_dir()
        || project_dir.join("supabase").join("config.toml").exists()
    {
        tags.push("supabase".into());
    }
    tags
}

/// Cards matching any of the given tags.
pub fn cards_for(tags: &[String]) -> Vec<TechCard> {
    TECH_CARDS
        .iter()
        .filter(|c| tags.iter().any(|t| t == c.tag))
        .copied()
        .collect()
}