roboticus 0.11.4

Autonomous agent runtime — HTTP API, CLI, WebSocket push, and migration engine
Documentation
use std::fs;

use roboticus_agent::skills::{LoadedSkill, SkillLoader};
use roboticus_agent::tools::{ScriptRunnerTool, ToolContext, ToolRegistry};
use roboticus_core::InputAuthority;
use roboticus_core::config::{FilesystemSecurityConfig, SkillsConfig};

#[tokio::test]
async fn hello_world_skill_loads_and_executes_via_run_script_tool() {
    let dir = tempfile::tempdir().expect("tempdir");
    let skills_dir = dir.path().join("skills");
    fs::create_dir_all(&skills_dir).expect("create skills dir");

    // Instruction skill discovery (skill catalog side).
    fs::write(
        skills_dir.join("hello_world.md"),
        r#"---
name: hello-world-skill
description: Says hello for integration test
triggers:
  keywords: ["hello world skill"]
  tool_names: []
  regex_patterns: []
priority: 5
---

Use run_script with hello_world.sh for greeting tasks.
"#,
    )
    .expect("write instruction skill");

    // Executable script target (runtime execution side).
    fs::write(
        skills_dir.join("hello_world.sh"),
        r#"#!/usr/bin/env bash
name="${1:-world}"
echo "hello skill ${name}"
"#,
    )
    .expect("write skill script");

    // Ensure loader sees the instruction skill.
    let loaded = SkillLoader::load_from_dir(&skills_dir).expect("load skills");
    assert!(
        loaded
            .iter()
            .any(|s| matches!(s, LoadedSkill::Instruction(skill, _, _) if skill.name == "hello-world-skill")),
        "expected hello-world instruction skill to load"
    );

    let skills_cfg = SkillsConfig {
        skills_dir: skills_dir.clone(),
        workspace_dir: Some(dir.path().to_path_buf()),
        sandbox_env: false,
        ..SkillsConfig::default()
    };

    let fs_cfg = FilesystemSecurityConfig::for_tests();
    let mut registry = ToolRegistry::new();
    registry.register(Box::new(ScriptRunnerTool::new(skills_cfg, fs_cfg)));

    let tool = registry
        .get("run_script")
        .expect("run_script tool should be registered");

    let ctx = ToolContext {
        session_id: "skill-int-test-session".to_string(),
        agent_id: "skill-int-test-agent".to_string(),
        agent_name: "Skill Integration Tester".to_string(),
        authority: InputAuthority::Creator,
        workspace_root: dir.path().to_path_buf(),
        tool_allowed_paths: vec![],
        channel: None,
        db: None,
        sandbox: roboticus_agent::tools::ToolSandboxSnapshot::default(),
    };

    let result = tool
        .execute(
            serde_json::json!({
                "path": "hello_world.sh",
                "args": ["roboticus"]
            }),
            &ctx,
        )
        .await
        .expect("run_script should execute hello_world.sh");

    assert!(
        result.output.contains("hello skill roboticus"),
        "unexpected run_script output: {}",
        result.output
    );
}