opencrabs 0.5.11

The autonomous, self-improving AI agent. Single Rust binary. Every channel. Recommended: the 40MB prebuilt binary for macOS, Linux and Windows: https://github.com/opencrabs/opencrabs/releases
//! Skills and `/onboard:<topic>` resolve instead of erroring (#889).
//!
//! Every recorded `slash_command` failure was one of these shapes:
//!
//! - `/servers`, `/channels` — skills on disk. `handle_user_command` consulted
//!   only `commands.toml`, so no skill could ever resolve and each one reached
//!   the "Unknown command" arm.
//! - `/onboard:voice` — the dispatch matched the bare `/onboard` only, so the
//!   colon form people actually type fell through.
//! - `/skills` — a real TUI command this tool cannot execute, which belonged in
//!   the guidance arm beside `/plan` and `/cowork`.
//!
//! None of these was a failing tool. The tool could not see what it was asked
//! about.
//!
//! Fixtures are synthetic and carry no user identifiers.

use crate::brain::skills::load_all_skills;

#[test]
fn skills_carry_the_slash_form_the_tool_matches_on() {
    // Resolution compares `slash_name`, so it must be the `/name` shape or the
    // lookup silently never matches.
    for s in load_all_skills() {
        assert!(
            s.slash_name.starts_with('/'),
            "skill {:?} has slash_name {:?}",
            s.name,
            s.slash_name
        );
        assert_eq!(s.slash_name, format!("/{}", s.name));
    }
}

#[test]
fn a_skill_lookup_by_slash_name_succeeds() {
    // The exact lookup the tool performs. Uses whichever skills exist rather
    // than hard-coding one, so the test does not depend on local install state.
    let skills = load_all_skills();
    if let Some(first) = skills.first() {
        let found = skills.iter().find(|s| s.slash_name == first.slash_name);
        assert!(
            found.is_some(),
            "a skill could not find itself by slash_name"
        );
    }
}

#[test]
fn direct_setup_commands_route_to_their_step() {
    // #1981: the direct commands are what users type now.
    use crate::brain::tools::slash_onboard::setup_step_for;
    for (cmd, step) in [
        ("/voice", "voice"),
        ("/channels", "channels"),
        ("/image", "image"),
        ("/brain", "brain"),
        ("/workspace", "workspace"),
        ("/daemon", "daemon"),
    ] {
        assert_eq!(setup_step_for(cmd), Some(step), "{cmd}");
    }
}

#[test]
fn the_legacy_onboard_colon_form_still_routes() {
    // #889 made the colon form resolve; #1981 keeps it as a fallback.
    use crate::brain::tools::slash_onboard::setup_step_for;
    for (cmd, step) in [
        ("/onboard:voice", "voice"),
        ("/onboard:channels", "channels"),
        ("/onboard:image", "image"),
    ] {
        assert_eq!(setup_step_for(cmd), Some(step), "{cmd}");
    }
}

#[test]
fn unrelated_commands_are_not_setup_steps() {
    use crate::brain::tools::slash_onboard::setup_step_for;
    for c in [
        "/onboard",
        "/onboard:",
        "/onboarding",
        "/on",
        "/board",
        "/models",
        "/voices",
    ] {
        assert_eq!(setup_step_for(c), None, "wrongly captured: {c}");
    }
}

#[test]
fn typed_args_reach_the_rendered_prompt() {
    // #1868: handle_user_command discarded the user's args as `_args`, so
    // `/servers staging` executed the skill text with no request attached
    // and the agent never saw what the human actually asked for.
    use crate::brain::tools::slash_command::with_user_args;
    assert_eq!(with_user_args("body".into(), ""), "body");
    assert_eq!(with_user_args("body".into(), "   "), "body");
    assert_eq!(
        with_user_args("body".into(), "fix tests"),
        "body\n\nUser args: fix tests"
    );
}

#[test]
fn the_args_parameter_is_named_not_discarded() {
    // Structural pin: the signature must thread args, never mask them with
    // `_args`. Owner rule: unused code is removed or used, not underscored.
    let src = include_str!("../brain/tools/slash_command.rs");
    assert!(
        src.contains("fn handle_user_command(&self, command: &str, args: &str)"),
        "handle_user_command must take args by name (#1868)"
    );
    assert!(
        !src.contains("_args: &str) -> Result<ToolResult>"),
        "the discarded-args shape must not come back"
    );
}