KS
Killer-Skills

duit-maintainer — how to use duit-maintainer how to use duit-maintainer, duit-maintainer setup guide, flutter ui toolkit, server-driven ui framework, duit-maintainer vs bdui, duit-maintainer install, what is duit-maintainer, duit-maintainer alternative, flutter_duit implementation

v1.0.0
GitHub

About this Skill

Perfect for Flutter Agents needing consistent and reusable UI components. duit-maintainer is a server-driven UI framework for Flutter, providing a consistent implementation pattern for UI widgets and components.

Features

Implements widget classes using Dart and Flutter
Creates widget implementation files in lib/src/ui/widgets/
Follows a consistent pattern for widget implementation
Uses ViewAttribute and Widget child properties
Supports server-driven UI framework for Flutter

# Core Topics

Duit-Foundation Duit-Foundation
[56]
[4]
Updated: 2/26/2026

Quality Score

Top 5%
41
Excellent
Based on code quality & docs
Installation
SYS Universal Install (Auto-Detect)
Cursor IDE Windsurf IDE VS Code IDE
> npx killer-skills add Duit-Foundation/flutter_duit/duit-maintainer

Agent Capability Analysis

The duit-maintainer MCP Server by Duit-Foundation is an open-source Categories.community integration for Claude and other AI agents, enabling seamless task automation and capability expansion. Optimized for how to use duit-maintainer, duit-maintainer setup guide, flutter ui toolkit.

Ideal Agent Persona

Perfect for Flutter Agents needing consistent and reusable UI components.

Core Value

Empowers agents to generate consistent UI components using Flutter and Dart, providing a server-driven UI framework with reusable widgets and ViewAttribute implementation.

Capabilities Granted for duit-maintainer MCP Server

Creating new widgets with consistent patterns
Implementing widget classes with ViewAttribute and child widgets
Maintaining consistent UI across flutter_duit widgets

! Prerequisites & Limits

  • Requires knowledge of Flutter and Dart
  • Limited to Flutter development environment
Project
SKILL.md
22.7 KB
.cursorrules
1.2 KB
package.json
240 B
Ready
UTF-8
SKILL.md
Readonly

Duit Maintainer

Quick Start

When working on flutter_duit widgets, follow these patterns consistently across all implementations.

Creating New Widgets

Step 1: Implement Widget Classes

Create widget implementation file in lib/src/ui/widgets/ following this pattern:

dart
1import "package:flutter/material.dart"; 2import "package:flutter_duit/flutter_duit.dart"; 3 4class Duit[WidgetName] extends StatelessWidget { 5 final ViewAttribute attributes; 6 final Widget child; // or final List<Widget?> children; 7 8 const Duit[WidgetName]({ 9 required this.attributes, 10 required this.child, // or this.children 11 super.key, 12 }); 13 14 @override 15 Widget build(BuildContext context) { 16 final attrs = attributes.payload; 17 return [WidgetName]( 18 key: ValueKey(attributes.id), 19 // ... widget-specific properties using attrs methods 20 child: child, // or children 21 ); 22 } 23} 24 25class DuitControlled[WidgetName] extends StatefulWidget { 26 final UIElementController controller; 27 final Widget child; // or final List<Widget?> children; 28 29 const DuitControlled[WidgetName]({ 30 required this.controller, 31 required this.child, // or this.children 32 super.key, 33 }); 34 35 @override 36 State<DuitControlled[WidgetName]> createState() => 37 _DuitControlled[WidgetName]State(); 38} 39 40class _DuitControlled[WidgetName]State extends State<DuitControlled[WidgetName]> 41 with ViewControllerChangeListener { 42 @override 43 void initState() { 44 attachStateToController(widget.controller); 45 super.initState(); 46 } 47 48 @override 49 Widget build(BuildContext context) { 50 return [WidgetName]( 51 key: ValueKey(widget.controller.id), 52 // ... widget-specific properties using attributes 53 child: widget.child, // or widget.children 54 ); 55 } 56}

Key Patterns:

  • Always create both Duit[WidgetName] (non-controlled) and DuitControlled[WidgetName] (controlled)
  • Controlled widgets must extend StatefulWidget and mix in ViewControllerChangeListener
  • The ViewControllerChangeListener mixin provides the late DuitDataSource attributes property, allowing direct access to attributes.getBool(), attributes.getDouble(), etc. in controlled widget state
  • Call attachStateToController(widget.controller) in initState
  • Use ValueKey(attributes.id) for non-controlled, ValueKey(widget.controller.id) for controlled
  • Access attributes via attributes.payload in non-controlled widgets
  • Access attributes via attributes (provided by ViewControllerChangeListener mixin) in controlled widgets
  • Themes are applied automatically via ViewAttribute.from() in widget_from_element.dart - themes are merged during ViewAttribute creation before reaching the widget implementation
  • Widget implementation code does NOT need to handle theme application explicitly - attributes received already have theme data merged

Step 2: Register Widget Type

Add to lib/src/ui/element_type.dart:

  1. Add enum value:
dart
1[widgetName]( 2 name: "[WidgetName]", 3 isControlledByDefault: false, // or true for interactive widgets 4 childRelation: 0, // 0=none, 1=single, 2=multiple, 3=component, 4=fragment 5),
  1. Add to lookup table (at end of file):
dart
1 "[WidgetName]": ElementType.[widgetName],

Child Relations:

  • 0: Leaf widgets (Text, Image, TextField)
  • 1: Single child wrapper (Container, Padding, Center)
  • 2: Multiple children (Row, Column, Stack, Visibility)
  • 3: Component content
  • 4: Fragment content

Step 3: Create Build Function

Add to lib/src/ui/widget_from_element.dart:

dart
1Widget _build[WidgetName](ElementPropertyView model) { 2 return switch (model.controlled) { 3 true => DuitControlled[WidgetName]( 4 controller: model.viewController, 5 child: _buildWidget(model.child), // or children 6 ), 7 false => Duit[WidgetName]( 8 attributes: model.attributes, 9 child: _buildWidget(model.child), // or children 10 ), 11 }; 12}

For multi-child widgets with nullable children:

dart
1Widget _build[WidgetName](ElementPropertyView model) { 2 final children = _mapToNullableWidgetList(model); 3 return switch (model.controlled) { 4 true => DuitControlled[WidgetName]( 5 controller: model.viewController, 6 children: children, 7 ), 8 false => Duit[WidgetName]( 9 attributes: model.attributes, 10 children: children, 11 ), 12 }; 13}

Step 4: Add to Build Function Lookup

Add to lib/src/ui/build_fn_lookup.dart:

dart
1 ElementType.[widgetName]: _build[WidgetName],

Step 5: Export Widget

Add to lib/src/ui/widgets/index.dart:

dart
1export "[widget_name].dart";

Step 6: Use AnimatedAttributes Mixin (for animatable properties)

If the widget has properties of types that support animation (e.g., width/height, Offset, and other types implementing .lerp), apply the AnimatedAttributes mixin.

The AnimatedAttributes mixin provides the mergeWithDataSource method that merges the widget's attributes with animated properties from the DuitAnimationContext if animation is active.

When to use AnimatedAttributes:

Apply this mixin when the widget has animatable properties such as:

  • Numeric values: width, height, opacity, scale
  • Position/offset: Offset, Size
  • Colors and other interpolatable types

Implementation pattern:

dart
1import "package:flutter_duit/flutter_duit.dart"; 2import "package:flutter_duit/src/animations/animated_props.dart"; 3 4class Duit[WidgetName] extends StatelessWidget with AnimatedAttributes { 5 final ViewAttribute attributes; 6 final Widget child; 7 8 const Duit[WidgetName]({ 9 required this.attributes, 10 required this.child, 11 super.key, 12 }); 13 14 @override 15 Widget build(BuildContext context) { 16 final attrs = mergeWithDataSource(context, attributes.payload); 17 return [WidgetName]( 18 key: ValueKey(attributes.id), 19 // ... widget-specific properties using attrs methods 20 // Properties will use animated values if animation is active 21 width: attrs.getDouble(key: "width"), 22 height: attrs.getDouble(key: "height"), 23 child: child, 24 ); 25 } 26}

For controlled widgets:

dart
1class _DuitControlled[WidgetName]State extends State<DuitControlled[WidgetName]> 2 with ViewControllerChangeListener { 3 // ... existing code ... 4 5 @override 6 Widget build(BuildContext context) { 7 final attrs = widget.mergeWithDataSource(context, attributes); 8 return [WidgetName]( 9 key: ValueKey(widget.controller.id), 10 // ... widget-specific properties 11 width: attrs.getDouble(key: "width"), 12 height: attrs.getDouble(key: "height"), 13 child: widget.child, 14 ); 15 } 16}

How mergeWithDataSource works:

The method checks for the following conditions:

  1. If the dataSource has a parentBuilderId
  2. If a DuitAnimationContext exists in the widget tree
  3. If the animation context's parentId matches the dataSource's parentBuilderId
  4. If there are affected properties to animate

If all conditions are met, it merges the animated values from the animation context streams with the current dataSource, allowing animated values to override static ones.

Key points:

  • Apply AnimatedAttributes mixin to both non-controlled and controlled widget classes
  • For non-controlled widgets: call mergeWithDataSource(context, attributes.payload)
  • For controlled widgets: call widget.mergeWithDataSource(context, attributes) (attributes provided by ViewControllerChangeListener)
  • Use the merged attributes to extract all animatable properties
  • The mixin gracefully handles cases where no animation is active (returns original dataSource)

Working with Themes

Theme Overview

Duit framework provides a theming system that allows you to define reusable styling for widgets. Themes are defined globally and applied automatically when widgets specify a theme attribute in their JSON definition.

How Themes Work

  1. Theme Definition: Themes are defined in a JSON structure and processed by DuitThemePreprocessor
  2. Theme Tokens: Each widget type has an associated ThemeToken class that defines which attributes can be themed
  3. Theme Application: When a widget specifies theme: "theme_name", the theme data is merged with the widget's attributes during ViewAttribute.from() creation
  4. Excluded Fields: Each ThemeToken has a list of excluded fields that should NOT be themed (e.g., callbacks, data, structural properties)

Theme System Components

Theme Tokens

Theme tokens are defined in lib/src/ui/theme/tokens.dart. Each token type excludes specific fields that should not be part of the theme:

  • TextThemeToken: Excludes data (the text content itself)
  • ImageThemeToken: Excludes src, byteData (image source data)
  • AttendedWidgetThemeToken: Excludes value (for Checkbox, Switch, etc.)
  • RadioThemeToken: Excludes animation-related properties
  • SliderThemeToken: Excludes callback properties (onChanged, onChangeStart, onChangeEnd)
  • ImplicitAnimatableThemeToken: Excludes onEnd callback
  • ExcludeGestureCallbacksThemeToken: Excludes all gesture callbacks (onTap, onDoubleTap, etc.)
  • ExcludeChildThemeToken: Excludes child subviews (title, actions, body, etc. for AppBar/Scaffold)
  • DynamicChildHolderThemeToken: Excludes childObjects, constructor
  • AnimatedPropOwnerThemeToken: Excludes animation-related properties
  • DefaultThemeToken: No exclusions (for widgets that can be fully themed)

Theme Preprocessor

The DuitThemePreprocessor class (lib/src/ui/theme/preprocessor.dart) handles theme token creation. It maps widget types to appropriate theme tokens:

dart
1final class DuitThemePreprocessor extends ThemePreprocessor { 2 const DuitThemePreprocessor({ 3 super.customWidgetTokenizer, 4 super.overrideWidgetTokenizer, 5 }); 6 7 @override 8 ThemeToken createToken(String widgetType, Map<String, dynamic> themeData) { 9 final type = ElementType.valueOrNull(widgetType); 10 return switch (type) { 11 ElementType.text => TextThemeToken(themeData), 12 ElementType.image => ImageThemeToken(themeData), 13 ElementType.checkbox || ElementType.switch_ || ElementType.textField || ElementType.meta => 14 AttendedWidgetThemeToken(themeData, widgetType), 15 // ... more mappings 16 _ => customWidgetTokenizer?.call(widgetType, themeData) ?? const UnknownThemeToken(), 17 }; 18 } 19}

Theme Initialization

Themes are initialized at application startup:

dart
1import 'package:duit_kernel/duit_kernel.dart'; 2import 'package:flutter_duit/src/ui/theme/preprocessor.dart'; 3 4// Define your themes 5final themePreprocessor = const DuitThemePreprocessor().tokenize({ 6 "primary_text": { 7 "type": "Text", 8 "data": { 9 "style": { 10 "fontSize": 16.0, 11 "fontWeight": "w600", 12 "color": "#333333", 13 }, 14 "textAlign": "left", 15 }, 16 }, 17 "secondary_text": { 18 "type": "Text", 19 "data": { 20 "style": { 21 "fontSize": 14.0, 22 "color": "#666666", 23 }, 24 }, 25 }, 26 "primary_button": { 27 "type": "ElevatedButton", 28 "data": { 29 "style": { 30 "backgroundColor": "#2196F3", 31 "foregroundColor": "#FFFFFF", 32 "padding": {"all": 16.0}, 33 }, 34 }, 35 }, 36}); 37 38// Initialize Duit with themes 39await DuitRegistry.initialize( 40 theme: themePreprocessor, 41);

Using Themes in Widget JSON

To apply a theme to a widget, use the theme attribute:

json
1{ 2 "type": "Text", 3 "id": "title", 4 "controlled": false, 5 "theme": "primary_text", 6 "attributes": { 7 "data": "Hello World" 8 } 9}

The theme will be merged with the widget's attributes. If both theme and attributes specify the same property, the behavior depends on overrideRule:

  • themeOverlay (default): Widget attributes take precedence
  • themePriority: Theme values take precedence
json
1{ 2 "type": "Text", 3 "id": "title", 4 "controlled": false, 5 "theme": "primary_text", 6 "overrideRule": "themePriority", 7 "attributes": { 8 "data": "Hello World", 9 "textAlign": "center" // This would be overridden by theme if themePriority 10 } 11}

Custom Theme Tokens

For custom widget types, you can create custom theme tokens:

dart
1final class CustomWidgetThemeToken extends ThemeToken { 2 CustomWidgetThemeToken(Map<String, dynamic> data) 3 : super( 4 const {}, // No excluded fields - allow all to be themed 5 data, 6 "CustomWidget", 7 ); 8}

Then provide it to the preprocessor:

dart
1final preprocessor = DuitThemePreprocessor( 2 customWidgetTokenizer: (type, themeData) { 3 switch (type) { 4 case "CustomWidget": 5 return CustomWidgetThemeToken(themeData); 6 default: 7 return null; 8 } 9 }, 10);

Or override existing widget tokenization:

dart
1final preprocessor = DuitThemePreprocessor( 2 overrideWidgetTokenizer: (type, themeData) { 3 switch (type) { 4 case "Text": 5 return CustomTextThemeToken(themeData); // Override default TextThemeToken 6 default: 7 return null; // Use default tokenization 8 } 9 }, 10);

Theme Token Selection Guide

When implementing a new widget, select the appropriate theme token type based on the widget's characteristics:

Widget TypeTheme TokenWhen to Use
Text widgetsTextThemeTokenText content should be excluded from theme
Image widgetsImageThemeTokenImage source (src, byteData) should be excluded
Interactive widgets with value (Checkbox, Switch, etc.)AttendedWidgetThemeTokenvalue field should be excluded
Radio buttonsRadioThemeTokenAnimation properties excluded
SlidersSliderThemeTokenCallbacks (onChanged, etc.) excluded
Implicit animationsImplicitAnimatableThemeTokenonEnd callback excluded
Gesture handlersExcludeGestureCallbacksThemeTokenAll gesture callbacks excluded
Layout widgets with children (AppBar, Scaffold)ExcludeChildThemeTokenChild subviews excluded
Lists/GridsDynamicChildHolderThemeTokenchildObjects excluded
Containers with animatable propertiesAnimatedPropOwnerThemeTokenAnimation properties excluded
Simple widgetsDefaultThemeTokenNo exclusions needed

Important Notes for Widget Implementation

Widget implementations do NOT need to handle themes explicitly. The theme merging happens in ViewAttribute.from() during widget tree construction. Your widget code receives attributes that already have theme data merged:

dart
1// In widget_from_element.dart - themes are applied here automatically 2Widget _buildText(ElementPropertyView model) { 3 // model.attributes already has theme data merged 4 return switch (model.controlled) { 5 true => DuitControlledText(controller: model.viewController), 6 false => DuitText(attributes: model.attributes), 7 }; 8} 9 10// In your widget implementation - just use attributes directly 11@override 12Widget build(BuildContext context) { 13 final attrs = attributes.payload; // or mergeWithDataSource for animated props 14 return Text( 15 attrs.getString(key: "data"), // Theme already merged into attributes 16 style: attrs.textStyle(), 17 ); 18}

Theme Testing

When testing widgets that support themes:

  1. Initialize the registry with theme preprocessor in setUpAll
  2. Create tests that verify theme attributes are applied
  3. Test override rules (themeOverlay vs themePriority)
dart
1import "package:duit_kernel/duit_kernel.dart"; 2import "package:flutter_duit/flutter_duit.dart"; 3import "package:flutter_test/flutter_test.dart"; 4 5void main() { 6 setUpAll(() async { 7 final proc = const DuitThemePreprocessor().tokenize({ 8 "text_theme": { 9 "type": "Text", 10 "data": { 11 "style": { 12 "fontSize": 24.0, 13 "color": "#FF0000", 14 }, 15 }, 16 }, 17 }); 18 await DuitRegistry.initialize(theme: proc); 19 }); 20 21 testWidgets("must apply theme attributes", (tester) async { 22 await tester.pumpWidget( 23 Directionality( 24 textDirection: TextDirection.ltr, 25 child: DuitViewHost.withDriver( 26 driver: XDriver.static({ 27 "type": "Text", 28 "id": "text_1", 29 "theme": "text_theme", 30 "attributes": {"data": "Themed Text"}, 31 }), 32 ), 33 ), 34 ); 35 36 final textWidget = tester.widget<Text>(find.byKey(const ValueKey("text_1"))); 37 expect(textWidget.style?.fontSize, equals(24.0)); 38 expect(textWidget.style?.color, equals(const Color(0xFFFF0000))); 39 }); 40}

Writing Tests

Test File Pattern

Create test file in test/ following this structure:

dart
1import "package:flutter/material.dart"; 2import "package:flutter_duit/flutter_duit.dart"; 3import "package:flutter_test/flutter_test.dart"; 4 5import "utils.dart"; 6 7Map<String, dynamic> _createWidget([bool isControlled = false]) { 8 return { 9 "type": "[WidgetName]", 10 "id": "[widget_name]_test", 11 "controlled": isControlled, 12 "attributes": { 13 // required attributes 14 }, 15 "child": { // or "children": [...] 16 "type": "Container", 17 "id": "con", 18 "controlled": false, 19 "attributes": {"color": "#DCDCDC", "width": 50, "height": 50}, 20 }, 21 }; 22} 23 24void main() { 25 group("Duit[WidgetName] widget tests", () { 26 testWidgets("check widget", (tester) async { 27 await tester.pumpWidget( 28 Directionality( 29 textDirection: TextDirection.ltr, 30 child: DuitViewHost.withDriver( 31 driver: XDriver.static( 32 _createWidget(), 33 ), 34 ), 35 ), 36 ); 37 await tester.pumpAndSettle(); 38 39 final widget = find.byKey(const ValueKey("[widget_name]_test")); 40 expect(widget, findsOneWidget); 41 }); 42 43 testWidgets( 44 "must update attributes", 45 (tester) async { 46 final driver = XDriver.static( 47 _createWidget(true), 48 ); 49 50 await pumpDriver(tester, driver.asInternalDriver); 51 52 final widget = find.byKey(const ValueKey("[widget_name]_test")); 53 expect(widget, findsOneWidget); 54 55 // Verify initial state 56 final initialWidget = tester.widget<[WidgetName]>(widget); 57 expect(initialWidget.property, expectedValue); 58 59 await driver.asInternalDriver.updateAttributes( 60 "[widget_name]_test", 61 { 62 "property": newValue, 63 }, 64 ); 65 66 await tester.pumpAndSettle(); 67 68 // Verify updated state 69 final updatedWidget = tester.widget<[WidgetName]>(widget); 70 expect(updatedWidget.property, newValue); 71 }, 72 ); 73 }); 74}

Test Guidelines:

  • Always use XDriver.static for widget tests
  • Create helper function _createWidget([bool isControlled = false])
  • Test both non-controlled and controlled versions
  • Use find.byKey(const ValueKey("...")) to locate widgets
  • For controlled widgets, test updateAttributes functionality
  • Use pumpDriver(tester, driver.asInternalDriver) for controlled widget tests
  • Import utils.dart for helper functions like pumpDriver

Attribute Access Patterns

When accessing attributes in widgets:

dart
1final attrs = attributes.payload; // or use attributes directly in controlled widgets 2 3// Double values 4width: attrs.tryGetDouble(key: "width") 5height: attrs.getDouble(key: "height") // throws if missing 6attrs.getDouble(key: "value", defaultValue: 0.0) 7 8// Boolean values 9visible: attrs.getBool("visible", defaultValue: true) 10 11// String values 12title: attrs.getString(key: "title") 13 14// Color values 15color: attrs.tryParseColor(key: "color") 16decoration: attrs.decoration() 17 18// EdgeInsets values 19padding: attrs.edgeInsets() 20margin: attrs.edgeInsets(key: "margin") 21 22// Alignment values 23alignment: attrs.alignment() 24 25// BoxConstraints values 26constraints: attrs.boxConstraints()

Code Review Checklist

When reviewing widget implementations:

  • Both Duit[WidgetName] and DuitControlled[WidgetName] classes exist
  • Controlled widget extends StatefulWidget and mixes ViewControllerChangeListener
  • Controlled widget state uses attributes property directly (provided by ViewControllerChangeListener mixin)
  • attachStateToController called in initState
  • Correct key usage: ValueKey(attributes.id) or ValueKey(widget.controller.id)
  • ElementType enum value added with correct childRelation
  • _stringToTypeLookupTable entry added
  • _build[WidgetName] function exists in widget_from_element.dart
  • Build function added to _buildFnLookup
  • Widget exported in widgets/index.dart
  • AnimatedAttributes mixin applied if widget has animatable properties (width, height, Offset, etc.)
  • mergeWithDataSource used to get merged attributes in widgets with AnimatedAttributes
  • Tests exist for both controlled and non-controlled versions
  • Tests use XDriver.static
  • Tests verify attribute updates for controlled widgets
  • Attribute access uses proper methods (tryGetDouble, getBool, etc.)
  • Default values provided where appropriate

Theme Support Checklist (REQUIRED for ALL widgets)

  • Widget type is added to DuitThemePreprocessor.createToken() in lib/src/ui/theme/preprocessor.dart
  • Appropriate theme token class is selected and instantiated for the widget type
  • Theme token correctly excludes fields that should NOT be themed (callbacks, data, structural properties)
  • Tests verify theme application for the widget (theme data merged with attributes)
  • Tests cover both themeOverlay and themePriority override rules if applicable
  • Widget implementation does NOT explicitly handle theme application (themes are applied automatically via ViewAttribute.from())
  • If widget has special theme requirements, custom theme token is documented in implementation comments

Widget Type Decision Flow

Determine child relation:

Widget has no children → childRelation: 0
Widget has one child → childRelation: 1
Widget has multiple children → childRelation: 2
Widget is component → childRelation: 3
Widget is fragment → childRelation: 4

Determine isControlledByDefault:

User interaction required (Button, TextField, etc.) → true
State management needed (Animation, etc.) → true
Static wrapper only → false

Common Mistakes to Avoid

  1. Missing controlled version: Always create both Duit[WidgetName] and DuitControlled[WidgetName]
  2. Wrong key type: Use ValueKey not just Key
  3. Forgetting attachStateToController: Must call this in initState for controlled widgets
  4. Wrong childRelation: Check widget signature for single vs multiple children
  5. Missing tests: Every widget must have tests covering both versions
  6. Not testing updates: Controlled widgets must test attribute updates
  7. Hard-coded values: Always use attrs methods to extract values from JSON
  8. Missing AnimatedAttributes for animatable properties: If a widget has properties that support animation (width, height, Offset, etc.), always apply the AnimatedAttributes mixin and use mergeWithDataSource to merge animated values

Related Skills

Looking for an alternative to duit-maintainer or building a Categories.community AI Agent? Explore these related open-source MCP Servers.

View All

widget-generator

Logo of f
f

widget-generator is an open-source AI agent skill for creating widget plugins that are injected into prompt feeds on prompts.chat. It supports two rendering modes: standard prompt widgets using default PromptCard styling and custom render widgets built as full React components.

149.6k
0
Design

chat-sdk

Logo of lobehub
lobehub

chat-sdk is a unified TypeScript SDK for building chat bots across multiple platforms, providing a single interface for deploying bot logic.

73.0k
0
Communication

zustand

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication

data-fetching

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication