context-bar 0.6.0

Usage HUD + API-equivalent cost estimator for AI coding agents (Claude Code, Codex CLI). Native macOS menubar app, terminal CLI, and Zed extension.
Documentation
use std::path::PathBuf;

use zed_extension_api::{self as zed, Result};

use crate::{context_engine::ContextEngine, hud, state_writer};

pub fn run(
    command: zed::SlashCommand,
    worktree: Option<&zed::Worktree>,
) -> Result<zed::SlashCommandOutput> {
    match command.name.as_str() {
        "hello" => run_hello(worktree),
        "brief" => run_brief(worktree),
        "hud" => run_hud(worktree),
        "doctor" => run_doctor(worktree),
        _ => Err(format!("unknown slash command: {}", command.name)),
    }
}

fn run_hello(worktree: Option<&zed::Worktree>) -> Result<zed::SlashCommandOutput> {
    let text = match worktree {
        Some(worktree) => {
            let root = worktree.root_path();
            format!(
                "Worktree `{root}` is connected. Context engine is available through `/brief`."
            )
        }
        None => "No worktree is attached. Run `/hello` inside a project.".to_string(),
    };

    Ok(zed::SlashCommandOutput {
        text,
        sections: vec![],
    })
}

fn run_brief(worktree: Option<&zed::Worktree>) -> Result<zed::SlashCommandOutput> {
    let worktree = worktree.ok_or_else(|| {
        "No worktree is attached. Run `/brief` inside a project-backed assistant thread."
            .to_string()
    })?;

    let snapshot = ContextEngine::generate(worktree)?;
    let root = PathBuf::from(worktree.root_path());

    let write_note = match state_writer::write(&root, &snapshot) {
        Ok(paths) => format!(
            "Artifacts updated under `{}` (`{}`, `{}`, `{}`, `{}`, `{}`). \
             Agents can read `AGENT.md` directly.",
            root.join(".context-bar").display(),
            paths.state_path.display(),
            paths.now_brief_path.display(),
            paths.session_brief_path.display(),
            paths.week_brief_path.display(),
            paths.agent_brief_path.display()
        ),
        Err(error) => format!(
            "Artifact write skipped: {error}. This prototype assumes Zed's extension runtime can write into the worktree, which is not verified yet."
        ),
    };

    let text = format!(
        concat!(
            "## ContextBar Brief\n\n",
            "- Branch: `{}`\n",
            "- Updated: `{}`\n",
            "- {}\n\n",
            "### Now\n",
            "{}\n\n",
            "Top files: {}\n\n",
            "### Session\n",
            "{}\n\n",
            "Focus areas: {}\n\n",
            "### Week\n",
            "{}\n\n",
            "Themes: {}\n\n",
            "### Assistant Memory\n",
            "{}\n"
        ),
        snapshot.branch,
        snapshot.updated_at,
        write_note,
        snapshot.now.resume_hint,
        format_list(&snapshot.now.top_files),
        snapshot.session.resume_hint,
        format_list(&snapshot.session.focus_areas),
        snapshot.week.resume_hint,
        format_list(&snapshot.week.themes),
        snapshot.assistant_memory.latest_summary
    );

    Ok(zed::SlashCommandOutput {
        text,
        sections: vec![],
    })
}

fn run_hud(worktree: Option<&zed::Worktree>) -> Result<zed::SlashCommandOutput> {
    let worktree = worktree.ok_or_else(|| {
        "No worktree is attached. Run `/hud` inside a project-backed assistant thread.".to_string()
    })?;

    let snapshot = ContextEngine::generate(worktree)?;
    let root = PathBuf::from(worktree.root_path());

    let footer = match state_writer::write(&root, &snapshot) {
        Ok(paths) => format!(
            "\n_HUD persisted to `{}` (auto-refreshes on every extension interaction)._\n",
            paths.hud_path.display()
        ),
        Err(error) => format!("\n_HUD write skipped: {error}._\n"),
    };

    let text = format!("{}{}", hud::render(&snapshot, &snapshot.usage), footer);

    Ok(zed::SlashCommandOutput {
        text,
        sections: vec![],
    })
}

fn run_doctor(worktree: Option<&zed::Worktree>) -> Result<zed::SlashCommandOutput> {
    let Some(worktree) = worktree else {
        return Ok(zed::SlashCommandOutput {
            text: concat!(
                "## ContextBar Doctor\n\n",
                "- worktree: missing\n",
                "- git binary: unknown\n",
                "- process exec: unknown\n",
                "- state write: unknown\n\n",
                "Run `/doctor` inside a project-backed assistant thread.\n"
            )
            .to_string(),
            sections: vec![],
        });
    };

    let root = PathBuf::from(worktree.root_path());
    let git_binary = worktree.which("git");
    let git_binary_line = match &git_binary {
        Some(path) => format!("- git binary: ok (`{path}`)"),
        None => "- git binary: missing from worktree PATH".to_string(),
    };

    let snapshot_result = ContextEngine::generate(worktree);
    let process_exec_line = match &snapshot_result {
        Ok(snapshot) => format!("- process exec: ok (branch `{}`)", snapshot.branch),
        Err(error) => format!("- process exec: failed ({error})"),
    };

    let state_write_line = match &snapshot_result {
        Ok(snapshot) => match state_writer::write(&root, snapshot) {
            Ok(paths) => format!("- state write: ok (`{}`)", paths.state_path.display()),
            Err(error) => format!("- state write: failed ({error})"),
        },
        Err(_) => "- state write: skipped because snapshot generation failed".to_string(),
    };

    let next_step = if snapshot_result.is_ok() {
        "Runtime basics look healthy. Next step: run `/brief` and inspect `.context-bar/` artifacts."
    } else {
        "If `process exec` failed, check Zed's `granted_extension_capabilities` for `process:exec` and review `Zed.log`."
    };

    let text = format!(
        concat!(
            "## ContextBar Doctor\n\n",
            "- worktree: ok (`{}`)\n",
            "{}\n",
            "{}\n",
            "{}\n\n",
            "{}\n"
        ),
        root.display(),
        git_binary_line,
        process_exec_line,
        state_write_line,
        next_step
    );

    Ok(zed::SlashCommandOutput {
        text,
        sections: vec![],
    })
}

fn format_list(items: &[String]) -> String {
    if items.is_empty() {
        "none".to_string()
    } else {
        items.join(", ")
    }
}