[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M719PEPQTEXD9WBA5TCW4T34":3,"skill-same:01M719PEPQTEXD9WBA5TCW4T34":65},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":45,"commitSha":46,"skillDirectory":47,"forksCount":14,"repositoryHtmlUrl":48,"repositoryHomepageUrl":6,"ownerHtmlUrl":49,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":50,"install":51,"resources":64},"01M719PEPQTEXD9WBA5TCW4T34","robius-app-architecture",null,"CRITICAL: Use for Robius app architecture patterns. Triggers on:\nTokio, async, submit_async_request, 异步, 架构,\nSignalToUI, Cx::post_action, worker task,\napp structure, MatchEvent, handle_startup","sapsapshen\u002Fai-memory-router","sapsapshen","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F20347959?v=4","01M6XDQEF1DP50DKF37H81J3J5","shenmian","01M6XDQEJPE41Y3DT2763X8ZJN",0,"MIT","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fglobal\u002Frobius-app-architecture\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"robius app architecture","architecture","app","patterns","startup","action","request","post","structure","task","unknown","critical","use","worker","triggers","async","submit","for","tokio","community","2026-11-10 10:12:30",false,"---\nname: robius-app-architecture\ndescription: |\n  CRITICAL: Use for Robius app architecture patterns. Triggers on:\n  Tokio, async, submit_async_request, 异步, 架构,\n  SignalToUI, Cx::post_action, worker task,\n  app structure, MatchEvent, handle_startup\nrisk: unknown\nsource: community\n---\n\n# Robius App Architecture Skill\n\nBest practices for structuring Makepad applications based on the Robrix and Moly codebases - production applications built with Makepad and Robius framework.\n\n**Source codebases:**\n- **Robrix**: Matrix chat client - complex sync\u002Fasync with background subscriptions\n- **Moly**: AI chat application - cross-platform (native + WASM) with streaming APIs\n\n## When to Use\n\nUse this skill when:\n- Building a Makepad application with async backend integration\n- Designing sync\u002Fasync communication patterns in Makepad\n- Structuring a Robius-style application\n- Keywords: robrix, robius, makepad app structure, async makepad, tokio makepad\n\n## Production Patterns\n\nFor production-ready async patterns, see the `_base\u002F` directory:\n\n| Pattern | Description |\n|---------|-------------|\n| 08-async-loading | Async data loading with loading states |\n| 09-streaming-results | Incremental results with SignalToUI |\n| 13-tokio-integration | Full tokio runtime integration |\n\n## Core Architecture Pattern\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│                     UI Thread (Makepad)                     │\n│  ┌─────────┐     ┌──────────┐     ┌──────────────────────┐ │\n│  │   App   │────▶│ WidgetRef │────▶│ Widget Tree (View)  │ │\n│  │ State   │     │    ui     │     │ Scope::with_data()  │ │\n│  └────┬────┘     └──────────┘     └──────────────────────┘ │\n│       │                                                     │\n│       │ submit_async_request()                              │\n│       ▼                                                     │\n│  ┌─────────────────┐          ┌─────────────────────────┐  │\n│  │ REQUEST_SENDER  │─────────▶│  Crossbeam SegQueue     │  │\n│  │ (MPSC Channel)  │          │  (Lock-free Updates)    │  │\n│  └─────────────────┘          └─────────────────────────┘  │\n└───────────────────────────────────┬─────────────────────────┘\n                                    │\n                    SignalToUI::set_ui_signal()\n                                    │\n┌───────────────────────────────────┴─────────────────────────┐\n│                   Tokio Runtime (Async)                      │\n│  ┌──────────────────────────────────────────────────────┐   │\n│  │           worker_task (Request Handler)               │   │\n│  │  - Receives Request from UI                           │   │\n│  │  - Spawns async tasks per request                     │   │\n│  │  - Posts actions back via Cx::post_action()           │   │\n│  └──────────────────────────────────────────────────────┘   │\n│  ┌──────────────────────────────────────────────────────┐   │\n│  │         Per-Item Subscriber Tasks                     │   │\n│  │  - Listens to external data stream                    │   │\n│  │  - Sends Update via crossbeam channel                 │   │\n│  │  - Calls SignalToUI::set_ui_signal() to wake UI       │   │\n│  └──────────────────────────────────────────────────────┘   │\n└─────────────────────────────────────────────────────────────┘\n```\n\n## App Structure\n\n### Top-Level App Definition\n\n```rust\nuse makepad_widgets::*;\n\nlive_design! {\n    use link::theme::*;\n    use link::widgets::*;\n\n    App = {{App}} {\n        ui: \u003CRoot>{\n            main_window = \u003CWindow> {\n                window: {inner_size: vec2(1280, 800), title: \"MyApp\"},\n                body = {\n                    \u002F\u002F Main content here\n                }\n            }\n        }\n    }\n}\n\napp_main!(App);\n\n#[derive(Live)]\npub struct App {\n    #[live] ui: WidgetRef,\n    #[rust] app_state: AppState,\n}\n\nimpl LiveRegister for App {\n    fn live_register(cx: &mut Cx) {\n        \u002F\u002F Order matters: register base widgets first\n        makepad_widgets::live_design(cx);\n        \u002F\u002F Then shared\u002Fcommon widgets\n        crate::shared::live_design(cx);\n        \u002F\u002F Then feature modules\n        crate::home::live_design(cx);\n    }\n}\n\nimpl LiveHook for App {\n    fn after_new_from_doc(&mut self, cx: &mut Cx) {\n        \u002F\u002F One-time initialization after widget tree is created\n    }\n}\n```\n\n### AppMain Implementation\n\n```rust\nimpl AppMain for App {\n    fn handle_event(&mut self, cx: &mut Cx, event: &Event) {\n        \u002F\u002F Forward to MatchEvent trait\n        self.match_event(cx, event);\n\n        \u002F\u002F Pass AppState through widget tree via Scope\n        let scope = &mut Scope::with_data(&mut self.app_state);\n        self.ui.handle_event(cx, event, scope);\n    }\n}\n```\n\n## Tokio Runtime Integration\n\n### Static Runtime Initialization\n\n```rust\nuse std::sync::Mutex;\nuse tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};\n\nstatic TOKIO_RUNTIME: Mutex\u003COption\u003Ctokio::runtime::Runtime>> = Mutex::new(None);\nstatic REQUEST_SENDER: Mutex\u003COption\u003CUnboundedSender\u003CAppRequest>>> = Mutex::new(None);\n\npub fn start_async_runtime() -> Result\u003Ctokio::runtime::Handle> {\n    let (request_sender, request_receiver) = tokio::sync::mpsc::unbounded_channel();\n\n    let rt_handle = TOKIO_RUNTIME.lock().unwrap()\n        .get_or_insert_with(|| {\n            tokio::runtime::Runtime::new()\n                .expect(\"Failed to create Tokio runtime\")\n        })\n        .handle()\n        .clone();\n\n    \u002F\u002F Store sender for UI thread to use\n    *REQUEST_SENDER.lock().unwrap() = Some(request_sender);\n\n    \u002F\u002F Spawn the main worker task\n    rt_handle.spawn(worker_task(request_receiver));\n\n    Ok(rt_handle)\n}\n```\n\n### Request Submission Pattern\n\n```rust\npub enum AppRequest {\n    FetchData { id: String },\n    SendMessage { content: String },\n    \u002F\u002F ... other request types\n}\n\n\u002F\u002F\u002F Submit a request from UI thread to async runtime\npub fn submit_async_request(req: AppRequest) {\n    if let Some(sender) = REQUEST_SENDER.lock().unwrap().as_ref() {\n        sender.send(req)\n            .expect(\"BUG: worker task receiver has died!\");\n    }\n}\n```\n\n### Worker Task Pattern\n\n```rust\nasync fn worker_task(mut request_receiver: UnboundedReceiver\u003CAppRequest>) -> Result\u003C()> {\n    while let Some(request) = request_receiver.recv().await {\n        match request {\n            AppRequest::FetchData { id } => {\n                \u002F\u002F Spawn a new task for each request\n                let _task = tokio::spawn(async move {\n                    let result = fetch_data(&id).await;\n                    \u002F\u002F Post result back to UI thread\n                    Cx::post_action(DataFetchedAction { id, result });\n                });\n            }\n            AppRequest::SendMessage { content } => {\n                let _task = tokio::spawn(async move {\n                    match send_message(&content).await {\n                        Ok(()) => Cx::post_action(MessageSentAction::Success),\n                        Err(e) => Cx::post_action(MessageSentAction::Failed(e)),\n                    }\n                });\n            }\n        }\n    }\n    Ok(())\n}\n```\n\n## Lock-Free Update Queue Pattern\n\nFor high-frequency updates from background tasks:\n\n```rust\nuse crossbeam_queue::SegQueue;\nuse makepad_widgets::SignalToUI;\n\npub enum DataUpdate {\n    NewItem { item: Item },\n    ItemChanged { id: String, changes: Changes },\n    Status { message: String },\n}\n\nstatic PENDING_UPDATES: SegQueue\u003CDataUpdate> = SegQueue::new();\n\n\u002F\u002F\u002F Called from background async tasks\npub fn enqueue_update(update: DataUpdate) {\n    PENDING_UPDATES.push(update);\n    SignalToUI::set_ui_signal();  \u002F\u002F Wake UI thread\n}\n\n\u002F\u002F In widget's handle_event:\nimpl Widget for MyWidget {\n    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {\n        \u002F\u002F Poll for updates on Signal events\n        if let Event::Signal = event {\n            while let Some(update) = PENDING_UPDATES.pop() {\n                match update {\n                    DataUpdate::NewItem { item } => {\n                        self.items.push(item);\n                        self.redraw(cx);\n                    }\n                    \u002F\u002F ... handle other updates\n                }\n            }\n        }\n    }\n}\n```\n\n## Startup Sequence\n\n```rust\nimpl MatchEvent for App {\n    fn handle_startup(&mut self, cx: &mut Cx) {\n        \u002F\u002F 1. Initialize logging\n        let _ = tracing_subscriber::fmt::try_init();\n\n        \u002F\u002F 2. Initialize app data directory\n        let _app_data_dir = crate::app_data_dir();\n\n        \u002F\u002F 3. Load persisted state\n        if let Err(e) = persistence::load_window_state(\n            self.ui.window(ids!(main_window)), cx\n        ) {\n            error!(\"Failed to load window state: {}\", e);\n        }\n\n        \u002F\u002F 4. Update UI based on loaded state\n        self.update_ui_visibility(cx);\n\n        \u002F\u002F 5. Start async runtime\n        let _rt_handle = crate::start_async_runtime().unwrap();\n    }\n}\n```\n\n## Shutdown Sequence\n\n```rust\nimpl AppMain for App {\n    fn handle_event(&mut self, cx: &mut Cx, event: &Event) {\n        if let Event::Shutdown = event {\n            \u002F\u002F Save window geometry\n            let window_ref = self.ui.window(ids!(main_window));\n            if let Err(e) = persistence::save_window_state(window_ref, cx) {\n                error!(\"Failed to save window state: {e}\");\n            }\n\n            \u002F\u002F Save app state\n            if let Some(user_id) = current_user_id() {\n                if let Err(e) = persistence::save_app_state(\n                    self.app_state.clone(), user_id\n                ) {\n                    error!(\"Failed to save app state: {e}\");\n                }\n            }\n        }\n        \u002F\u002F ... rest of event handling\n    }\n}\n```\n\n## Best Practices\n\n1. **Separation of Concerns**: Keep UI logic on the main thread, async operations in Tokio runtime\n2. **Request\u002FResponse Pattern**: Use typed enums for requests and actions\n3. **Lock-Free Updates**: Use `crossbeam::SegQueue` for high-frequency background updates\n4. **SignalToUI**: Always call `SignalToUI::set_ui_signal()` after enqueueing updates\n5. **Cx::post_action()**: Use for async task results that need action handling\n6. **Scope::with_data()**: Pass shared state through widget tree\n7. **Module Registration Order**: Register base widgets before dependent modules in `live_register()`\n\n## Reference Files\n\n- `references\u002Ftokio-integration.md` - Detailed Tokio runtime patterns (Robrix)\n- `references\u002Fchannel-patterns.md` - Channel communication patterns (Robrix)\n- `references\u002Fmoly-async-patterns.md` - Cross-platform async patterns (Moly)\n  - `PlatformSend` trait for native\u002FWASM compatibility\n  - `UiRunner` for async defer operations\n  - `AbortOnDropHandle` for task cancellation\n  - `ThreadToken` for non-Send types on WASM\n  - `spawn()` platform-agnostic function\n","# Robius App Architecture Skill\n\nBest practices for structuring Makepad applications based on the Robrix and Moly codebases - production applications built with Makepad and Robius framework.\n\n**Source codebases:**\n- **Robrix**: Matrix chat client - complex sync\u002Fasync with background subscriptions\n- **Moly**: AI chat application - cross-platform (native + WASM) with streaming APIs\n\n## When to Use\n\nUse this skill when:\n- Building a Makepad application with async backend integration\n- Designing sync\u002Fasync communication patterns in Makepad\n- Structuring a Robius-style application\n- Keywords: robrix, robius, makepad app structure, async makepad, tokio makepad\n\n## Production Patterns\n\nFor production-ready async patterns, see the `_base\u002F` directory:\n\n| Pattern | Description |\n|---------|-------------|\n| 08-async-loading | Async data loading with loading states |\n| 09-streaming-results | Incremental results with SignalToUI |\n| 13-tokio-integration | Full tokio runtime integration |\n\n## Core Architecture Pattern\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│                     UI Thread (Makepad)                     │\n│  ┌─────────┐     ┌──────────┐     ┌──────────────────────┐ │\n│  │   App   │────▶│ WidgetRef │────▶│ Widget Tree (View)  │ │\n│  │ State   │     │    ui     │     │ Scope::with_data()  │ │\n│  └────┬────┘     └──────────┘     └──────────────────────┘ │\n│       │                                                     │\n│       │ submit_async_request()                              │\n│       ▼                                                     │\n│  ┌─────────────────┐          ┌─────────────────────────┐  │\n│  │ REQUEST_SENDER  │─────────▶│  Crossbeam SegQueue     │  │\n│  │ (MPSC Channel)  │          │  (Lock-free Updates)    │  │\n│  └─────────────────┘          └─────────────────────────┘  │\n└───────────────────────────────────┬─────────────────────────┘\n                                    │\n                    SignalToUI::set_ui_signal()\n                                    │\n┌───────────────────────────────────┴─────────────────────────┐\n│                   Tokio Runtime (Async)                      │\n│  ┌──────────────────────────────────────────────────────┐   │\n│  │           worker_task (Request Handler)               │   │\n│  │  - Receives Request from UI                           │   │\n│  │  - Spawns async tasks per request                     │   │\n│  │  - Posts actions back via Cx::post_action()           │   │\n│  └──────────────────────────────────────────────────────┘   │\n│  ┌──────────────────────────────────────────────────────┐   │\n│  │         Per-Item Subscriber Tasks                     │   │\n│  │  - Listens to external data stream                    │   │\n│  │  - Sends Update via crossbeam channel                 │   │\n│  │  - Calls SignalToUI::set_ui_signal() to wake UI       │   │\n│  └──────────────────────────────────────────────────────┘   │\n└─────────────────────────────────────────────────────────────┘\n```\n\n## App Structure\n\n### Top-Level App Definition\n\n```rust\nuse makepad_widgets::*;\n\nlive_design! {\n    use link::theme::*;\n    use link::widgets::*;\n\n    App = {{App}} {\n        ui: \u003CRoot>{\n            main_window = \u003CWindow> {\n                window: {inner_size: vec2(1280, 800), title: \"MyApp\"},\n                body = {\n                    \u002F\u002F Main content here\n                }\n            }\n        }\n    }\n}\n\napp_main!(App);\n\n#[derive(Live)]\npub struct App {\n    #[live] ui: WidgetRef,\n    #[rust] app_state: AppState,\n}\n\nimpl LiveRegister for App {\n    fn live_register(cx: &mut Cx) {\n        \u002F\u002F Order matters: register base widgets first\n        makepad_widgets::live_design(cx);\n        \u002F\u002F Then shared\u002Fcommon widgets\n        crate::shared::live_design(cx);\n        \u002F\u002F Then feature modules\n        crate::home::live_design(cx);\n    }\n}\n\nimpl LiveHook for App {\n    fn after_new_from_doc(&mut self, cx: &mut Cx) {\n        \u002F\u002F One-time initialization after widget tree is created\n    }\n}\n```\n\n### AppMain Implementation\n\n```rust\nimpl AppMain for App {\n    fn handle_event(&mut self, cx: &mut Cx, event: &Event) {\n        \u002F\u002F Forward to MatchEvent trait\n        self.match_event(cx, event);\n\n        \u002F\u002F Pass AppState through widget tree via Scope\n        let scope = &mut Scope::with_data(&mut self.app_state);\n        self.ui.handle_event(cx, event, scope);\n    }\n}\n```\n\n## Tokio Runtime Integration\n\n### Static Runtime Initialization\n\n```rust\nuse std::sync::Mutex;\nuse tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};\n\nstatic TOKIO_RUNTIME: Mutex\u003COption\u003Ctokio::runtime::Runtime>> = Mutex::new(None);\nstatic REQUEST_SENDER: Mutex\u003COption\u003CUnboundedSender\u003CAppRequest>>> = Mutex::new(None);\n\npub fn start_async_runtime() -> Result\u003Ctokio::runtime::Handle> {\n    let (request_sender, request_receiver) = tokio::sync::mpsc::unbounded_channel();\n\n    let rt_handle = TOKIO_RUNTIME.lock().unwrap()\n        .get_or_insert_with(|| {\n            tokio::runtime::Runtime::new()\n                .expect(\"Failed to create Tokio runtime\")\n        })\n        .handle()\n        .clone();\n\n    \u002F\u002F Store sender for UI thread to use\n    *REQUEST_SENDER.lock().unwrap() = Some(request_sender);\n\n    \u002F\u002F Spawn the main worker task\n    rt_handle.spawn(worker_task(request_receiver));\n\n    Ok(rt_handle)\n}\n```\n\n### Request Submission Pattern\n\n```rust\npub enum AppRequest {\n    FetchData { id: String },\n    SendMessage { content: String },\n    \u002F\u002F ... other request types\n}\n\n\u002F\u002F\u002F Submit a request from UI thread to async runtime\npub fn submit_async_request(req: AppRequest) {\n    if let Some(sender) = REQUEST_SENDER.lock().unwrap().as_ref() {\n        sender.send(req)\n            .expect(\"BUG: worker task receiver has died!\");\n    }\n}\n```\n\n### Worker Task Pattern\n\n```rust\nasync fn worker_task(mut request_receiver: UnboundedReceiver\u003CAppRequest>) -> Result\u003C()> {\n    while let Some(request) = request_receiver.recv().await {\n        match request {\n            AppRequest::FetchData { id } => {\n                \u002F\u002F Spawn a new task for each request\n                let _task = tokio::spawn(async move {\n                    let result = fetch_data(&id).await;\n                    \u002F\u002F Post result back to UI thread\n                    Cx::post_action(DataFetchedAction { id, result });\n                });\n            }\n            AppRequest::SendMessage { content } => {\n                let _task = tokio::spawn(async move {\n                    match send_message(&content).await {\n                        Ok(()) => Cx::post_action(MessageSentAction::Success),\n                        Err(e) => Cx::post_action(MessageSentAction::Failed(e)),\n                    }\n                });\n            }\n        }\n    }\n    Ok(())\n}\n```\n\n## Lock-Free Update Queue Pattern\n\nFor high-frequency updates from background tasks:\n\n```rust\nuse crossbeam_queue::SegQueue;\nuse makepad_widgets::SignalToUI;\n\npub enum DataUpdate {\n    NewItem { item: Item },\n    ItemChanged { id: String, changes: Changes },\n    Status { message: String },\n}\n\nstatic PENDING_UPDATES: SegQueue\u003CDataUpdate> = SegQueue::new();\n\n\u002F\u002F\u002F Called from background async tasks\npub fn enqueue_update(update: DataUpdate) {\n    PENDING_UPDATES.push(update);\n    SignalToUI::set_ui_signal();  \u002F\u002F Wake UI thread\n}\n\n\u002F\u002F In widget's handle_event:\nimpl Widget for MyWidget {\n    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {\n        \u002F\u002F Poll for updates on Signal events\n        if let Event::Signal = event {\n            while let Some(update) = PENDING_UPDATES.pop() {\n                match update {\n                    DataUpdate::NewItem { item } => {\n                        self.items.push(item);\n                        self.redraw(cx);\n                    }\n                    \u002F\u002F ... handle other updates\n                }\n            }\n        }\n    }\n}\n```\n\n## Startup Sequence\n\n```rust\nimpl MatchEvent for App {\n    fn handle_startup(&mut self, cx: &mut Cx) {\n        \u002F\u002F 1. Initialize logging\n        let _ = tracing_subscriber::fmt::try_init();\n\n        \u002F\u002F 2. Initialize app data directory\n        let _app_data_dir = crate::app_data_dir();\n\n        \u002F\u002F 3. Load persisted state\n        if let Err(e) = persistence::load_window_state(\n            self.ui.window(ids!(main_window)), cx\n        ) {\n            error!(\"Failed to load window state: {}\", e);\n        }\n\n        \u002F\u002F 4. Update UI based on loaded state\n        self.update_ui_visibility(cx);\n\n        \u002F\u002F 5. Start async runtime\n        let _rt_handle = crate::start_async_runtime().unwrap();\n    }\n}\n```\n\n## Shutdown Sequence\n\n```rust\nimpl AppMain for App {\n    fn handle_event(&mut self, cx: &mut Cx, event: &Event) {\n        if let Event::Shutdown = event {\n            \u002F\u002F Save window geometry\n            let window_ref = self.ui.window(ids!(main_window));\n            if let Err(e) = persistence::save_window_state(window_ref, cx) {\n                error!(\"Failed to save window state: {e}\");\n            }\n\n            \u002F\u002F Save app state\n            if let Some(user_id) = current_user_id() {\n                if let Err(e) = persistence::save_app_state(\n                    self.app_state.clone(), user_id\n                ) {\n                    error!(\"Failed to save app state: {e}\");\n                }\n            }\n        }\n        \u002F\u002F ... rest of event handling\n    }\n}\n```\n\n## Best Practices\n\n1. **Separation of Concerns**: Keep UI logic on the main thread, async operations in Tokio runtime\n2. **Request\u002FResponse Pattern**: Use typed enums for requests and actions\n3. **Lock-Free Updates**: Use `crossbeam::SegQueue` for high-frequency background updates\n4. **SignalToUI**: Always call `SignalToUI::set_ui_signal()` after enqueueing updates\n5. **Cx::post_action()**: Use for async task results that need action handling\n6. **Scope::with_data()**: Pass shared state through widget tree\n7. **Module Registration Order**: Register base widgets before dependent modules in `live_register()`\n\n## Reference Files\n\n- `references\u002Ftokio-integration.md` - Detailed Tokio runtime patterns (Robrix)\n- `references\u002Fchannel-patterns.md` - Channel communication patterns (Robrix)\n- `references\u002Fmoly-async-patterns.md` - Cross-platform async patterns (Moly)\n  - `PlatformSend` trait for native\u002FWASM compatibility\n  - `UiRunner` for async defer operations\n  - `AbortOnDropHandle` for task cancellation\n  - `ThreadToken` for non-Send types on WASM\n  - `spawn()` platform-agnostic function\n",{"name":5,"risk":29,"source":38,"description":44},"CRITICAL: Use for Robius app architecture patterns. Triggers on:\nTokio, async, submit_async_request, 异步, 架构,\nSignalToUI, Cx::post_action, worker task,\napp structure, MatchEvent, handle_startup\n","main","7a38d8e7073e61c16eda85681cb32fb68a8657a5","skills-store\u002Fglobal\u002Frobius-app-architecture","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router","https:\u002F\u002Fgithub.com\u002Fsapsapshen","2026-04-14 21:12:12",{"source":8,"skillName":5,"commands":52,"prompt":62,"zipAvailable":63},[53,56,59],{"tool":54,"command":55},"npx","npx skills add sapsapshen\u002Fai-memory-router --skill robius-app-architecture",{"tool":57,"command":58},"bunx","bunx skills add sapsapshen\u002Fai-memory-router --skill robius-app-architecture",{"tool":60,"command":61},"pnpm","pnpm dlx skills add sapsapshen\u002Fai-memory-router --skill robius-app-architecture","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fglobal\u002Frobius-app-architecture\u002FSKILL.md",true,[],[66,90,116,126,139,158],{"skillNo":67,"name":68,"nameZh":6,"description":69,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":70,"categories":71,"keywords":77,"publishedAt":39},"01M71ATQRKG48SH06FQ697T5G9","wechat-search","Search WeChat Official Account articles using OpenClaw's web search and fetch capabilities with compliance-focused design.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fwechat-search-release\u002FSKILL.md",[72],{"categoryNo":73,"categoryName":74,"taxonomyType":75,"taxonomyTypeDesc":76},"01JBSEEDCAT000000000000037","律师与法务","PROFESSION","适用职业",[78,79,80,81,82,83,84,85,86,87,88,89],"wechat search","wechat","search","capabilities","openclaw","design","web search","compliance","web","official","fetch","with",{"skillNo":91,"name":92,"nameZh":6,"description":93,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":94,"categories":95,"keywords":96,"publishedAt":39},"01M71ATNAVGZB35PFYM2GC5N00","trend-tap","Real-time trending topics aggregator across 7 platforms (X\u002FTwitter, Reddit, Google Trends, Hacker News, Zhihu, Bilibili, Weibo). Trigger: when user says 'trend', 'trends', 'trend tap', '热点', '热搜', or asks what is trending\u002Fhot. Do NOT use web_search — this skill already fetches live data from all platforms with zero API keys.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftrend-tap\u002FSKILL.md",[],[97,98,99,100,101,102,80,103,104,105,106,107,108,109,110,111,112,113,114,115],"trend","trend tap","tap","api keys","Bash","time","trending","hacker","aggregator","do","when","brew","live","data","google","install","read","across","api",{"skillNo":117,"name":118,"nameZh":6,"description":119,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":120,"categories":121,"keywords":122,"publishedAt":39},"01M71ATJ9AM5KM15AA8PS302EB","trend-radar","Real-time trending topics aggregator across 7 platforms (X\u002FTwitter, Reddit, Google Trends, Hacker News, Zhihu, Bilibili, Weibo). Trigger: when user says 'trend', 'trends', 'trend pulse', '热点', '热搜', or asks what is trending\u002Fhot. Do NOT use web_search — this skill already fetches live data from all platforms with zero API keys.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftrend-radar\u002FSKILL.md",[],[123,124,97,108,109,110,111,101,80,113,125,114,115,107,105,104,103,112,102,100],"trend radar","radar","pulse",{"skillNo":127,"name":128,"nameZh":6,"description":129,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":130,"categories":131,"keywords":132,"publishedAt":39},"01M71ATGF095N3EJM9G8SD0BB5","tech-news-daily","每日科技新闻汇——从全球多个 RSS 源抓取最新科技新闻，翻译（英→中）、去重、排版成每日简报。当用户询问科技新闻、科技新鲜事、每日科技、技术动态、Daily Tech News 或类似表达时触发此技能。","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftech-news-daily\u002FSKILL.md",[],[133,134,135,136,137,138],"news","tech","tech news daily","daily","rss","翻译",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":150,"publishedAt":39},"01M71ATFAEAMQ17M70NQNY7SR8","summarize","Summarize URLs or files with the summarize CLI (web, PDFs, images, audio, YouTube).","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fsummarize\u002FSKILL.md",[145],{"categoryNo":146,"categoryName":147,"taxonomyType":148,"taxonomyTypeDesc":149},"01JBSEEDCAT000000000000021","资料检索与研究","SCENARIO","使用场景",[141,86,151,152,153,89,108,154,155,156,99,157,112],"Audio","cli","youtube","images","https","files","pdfs",{"skillNo":159,"name":160,"nameZh":6,"description":161,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":162,"categories":163,"keywords":164,"publishedAt":39},"01M71ATDNS3R0M4H936PACQAWZ","sonoscli","Control Sonos speakers (discover\u002Fstatus\u002Fplay\u002Fvolume\u002Fgroup).","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fsonoscli\u002FSKILL.md",[],[160,165,166,167,112,168,155,169,170,171,172,173],"status","play","go","latest","github","discover","control","cmd","volume"]