arcature-cli 2026.2.0

Developer lifecycle CLI for Arcature applications.
Documentation
//! `pages` tool — the page-contract identities from the UAG.
//!
//! Returns every page identity the UAG carries (the stable frontend page
//! names aggregated from `#[page("...")]` declarations). Takes no arguments.
//! The UAG content is developer-authored (trusted), so returning all pages
//! is not an abuse vector.

use crate::commands::mcp::capability::CapabilitySet;
use crate::commands::mcp::error::McpError;
use crate::commands::mcp::registry::ToolContext;
use serde::Serialize;
use serde_json::Value;

#[derive(Serialize)]
struct PagesResult<'a> {
    pages: &'a [arcature_build::uag::schema::PageEntry],
}

pub(crate) fn call(
    _arguments: &Value,
    _capabilities: &CapabilitySet,
    context: &ToolContext,
) -> Result<Value, McpError> {
    let result = PagesResult {
        pages: &context.uag.pages,
    };
    serde_json::to_value(result).map_err(McpError::from)
}

#[cfg(test)]
mod tests {
    use super::*;
    use arcature_build::uag::Uag;
    use arcature_build::uag::schema::PageEntry;
    use std::collections::BTreeMap;

    fn ctx_with_pages(names: &[&str]) -> ToolContext {
        let pages: Vec<PageEntry> = names
            .iter()
            .map(|n| PageEntry { name: (*n).into() })
            .collect();
        ToolContext {
            uag: Uag {
                schema_version: 1,
                application: "App".into(),
                framework_version: "2026.1.0".into(),
                modules: BTreeMap::new(),
                routes: vec![],
                services: vec![],
                pages,
            },
        }
    }

    #[test]
    fn returns_all_pages() {
        let ctx = ctx_with_pages(&["Links/Index", "Links/Show", "NewLink"]);
        let value = call(
            &Value::Null,
            &CapabilitySet::from_options(&crate::cli::McpOptions::default()),
            &ctx,
        )
        .expect("ok");
        let arr = value["pages"].as_array().expect("array");
        assert_eq!(arr.len(), 3);
        assert_eq!(arr[0]["name"], "Links/Index");
    }

    #[test]
    fn empty_uag_returns_empty_pages() {
        let ctx = ctx_with_pages(&[]);
        let value = call(
            &Value::Null,
            &CapabilitySet::from_options(&crate::cli::McpOptions::default()),
            &ctx,
        )
        .expect("ok");
        assert!(value["pages"].as_array().expect("array").is_empty());
    }
}