Communitygithub.com

dart-flutter-patterns

Patrones de Dart y Flutter listos para producción: seguridad nula, estado inmutable, composición asíncrona, arquitectura de widgets, gestores de estado populares (BLoC, Riverpod, Provider), navegación GoRouter, redes con Dio, generación de código Freezed y arquitectura limpia.

¿Qué es dart-flutter-patterns?

Este skill proporciona un conjunto completo y práctico de patrones de Dart y Flutter, diseñados para ser reutilizados directamente en proyectos reales. Cubre desde los fundamentos de seguridad nula y estado inmutable con `freezed`, hasta la composición asíncrona segura con `Future.wait` y el manejo correcto del `BuildContext` después de un `await`. Para la gestión de estado, incluye ejemplos detallados de BLoC/Cubit y Riverpod, incluyendo providers derivados y notificadores con mutaciones complejas. En navegación, muestra cómo implementar GoRouter con guardias de autenticación reactivas mediante `refreshListenable`. Además, aborda la comunicación HTTP con Dio, incluyendo interceptores y renovación de tokens con protección contra bucles infinitos, y finaliza con una arquitectura sólida de manejo de errores y testing unitario y de widgets. Es ideal para cualquier desarrollador que quiera escribir código Flutter robusto, mantenible y con las mejores prácticas de la comunidad.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/dart-flutter-patterns

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Patrones de Dart/Flutter

Cuándo usar

Usa este skill cuando:

  • Inicies una nueva funcionalidad en Flutter y necesites patrones idiomáticos para gestión de estado, navegación o acceso a datos.
  • Estés revisando o escribiendo código Dart y necesites orientación sobre seguridad nula, tipos sellados o composición asíncrona.
  • Estés configurando un nuevo proyecto Flutter y decidiendo entre BLoC, Riverpod o Provider.
  • Implementes clientes HTTP seguros, integración con WebView o almacenamiento local.
  • Escribas tests para widgets Flutter, Cubits o providers de Riverpod.
  • Estés conectando GoRouter con guardias de autenticación.

Cómo funciona

Este skill proporciona patrones de código Dart/Flutter listos para copiar y pegar, organizados por tema:

  1. Seguridad nula: evitar !, preferir ?./??/pattern matching.
  2. Estado inmutable: clases selladas, freezed, copyWith.
  3. Composición asíncrona: Future.wait concurrente, BuildContext seguro después de await.
  4. Arquitectura de widgets: extraer a clases (no a métodos), propagación de const, reconstrucciones acotadas.
  5. Gestión de estado: eventos BLoC/Cubit, notificadores y providers derivados de Riverpod.
  6. Navegación: GoRouter con guardias de autenticación reactivas mediante refreshListenable.
  7. Redes: Dio con interceptores, renovación de tokens con protección de reintento único.
  8. Manejo de errores: captura global, ErrorWidget.builder, integración con crashlytics.
  9. Testing: unitario (BLoC test), de widgets (anulaciones de ProviderScope), fakes en lugar de mocks.

Ejemplos

// Estado sellado — previene estados imposibles
sealed class AsyncState<T> {}
final class Loading<T> extends AsyncState<T> {}
final class Success<T> extends AsyncState<T> { final T data; const Success(this.data); }
final class Failure<T> extends AsyncState<T> { final Object error; const Failure(this.error); }

// GoRouter con redirección reactiva de autenticación
final router = GoRouter(
  refreshListenable: GoRouterRefreshStream(authCubit.stream),
  redirect: (context, state) {
    final authed = context.read<AuthCubit>().state is AuthAuthenticated;
    if (!authed && !state.matchedLocation.startsWith('/login')) return '/login';
    return null;
  },
  routes: [...],
);

// Provider derivado de Riverpod con firstWhereOrNull seguro
@riverpod
double cartTotal(Ref ref) {
  final cart = ref.watch(cartNotifierProvider);
  final products = ref.watch(productsProvider).valueOrNull ?? [];
  return cart.fold(0.0, (total, item) {
    final product = products.firstWhereOrNull((p) => p.id == item.productId);
    return total + (product?.price ?? 0) * item.quantity;
  });
}

Patrones prácticos y listos para producción para aplicaciones Dart y Flutter. Agnóstico en cuanto a librerías cuando es posible, con cobertura explícita de los paquetes más comunes del ecosistema.


1. Fundamentos de Seguridad Nula

Preferir Patrones sobre el Operador Bang

// MALO — falla en tiempo de ejecución si es nulo
final name = user!.name;

// BUENO — proporciona valor por defecto
final name = user?.name ?? 'Desconocido';

// BUENO — pattern matching de Dart 3 (preferido para casos complejos)
final display = switch (user) {
  User(:final name, :final email) => '$name <$email>',
  null => 'Invitado',
};

// BUENO — retorno temprano con guardia
String getUserName(User? user) {
  if (user == null) return 'Desconocido';
  return user.name; // promovido a no nulo después de la comprobación
}

Evitar el Uso Excesivo de late

// MALO — difiere el error nulo al tiempo de ejecución
late String userId;

// BUENO — anulable con inicialización explícita
String? userId;

// ACEPTABLE — usar late solo cuando la inicialización está garantizada antes del primer acceso
// (ej. en initState() antes de cualquier interacción con widgets)
late final AnimationController _controller;

@override
void initState() {
  super.initState();
  _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 300));
}

2. Estado Inmutable

Clases Selladas para Jerarquías de Estado

sealed class UserState {}

final class UserInitial extends UserState {}

final class UserLoading extends UserState {}

final class UserLoaded extends UserState {
  const UserLoaded(this.user);
  final User user;
}

final class UserError extends UserState {
  const UserError(this.message);
  final String message;
}

// Switch exhaustivo — el compilador obliga a cubrir todas las ramas
Widget buildFrom(UserState state) => switch (state) {
  UserInitial() => const SizedBox.shrink(),
  UserLoading() => const CircularProgressIndicator(),
  UserLoaded(:final user) => UserCard(user: user),
  UserError(:final message) => ErrorText(message),
};

Freezed para Inmutabilidad sin Boilerplate

import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@freezed
class User with _$User {
  const factory User({
    required String id,
    required String name,
    required String email,
    @Default(false) bool isAdmin,
  }) = _User;

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

// Uso
final user = User(id: '1', name: 'Alicia', email: '[email protected]');
final updated = user.copyWith(name: 'Alicia García'); // actualización inmutable
final json = user.toJson();
final fromJson = User.fromJson(json);

3. Composición Asíncrona

Concurrencia Estructurada con Future.wait

Future<DashboardData> loadDashboard(UserRepository users, OrderRepository orders) async {
  // Ejecutar concurrentemente — no esperar secuencialmente
  final (userList, orderList) = await (
    users.getAll(),
    orders.getRecent(),
  ).wait; // Desestructuración con registros de Dart 3 + extensión Future.wait

  return DashboardData(users: userList, orders: orderList);
}

Patrones con Streams

// El repositorio expone streams reactivos para datos en vivo
Stream<List<Item>> watchCartItems() => _db
    .watchTable('cart_items')
    .map((rows) => rows.map(Item.fromRow).toList());

// En la capa de widgets — declarativo, sin suscripción manual
StreamBuilder<List<Item>>(
  stream: cartRepository.watchCartItems(),
  builder: (context, snapshot) => switch (snapshot) {
    AsyncSnapshot(connectionState: ConnectionState.waiting) =>
        const CircularProgressIndicator(),
    AsyncSnapshot(:final error?) => ErrorWidget(error.toString()),
    AsyncSnapshot(:final data?) => CartList(items: data),
    _ => const SizedBox.shrink(),
  },
)

BuildContext Después de Await

// CRÍTICO — siempre verificar mounted después de cualquier await en StatefulWidget
Future<void> _handleSubmit() async {
  setState(() => _isLoading = true);
  try {
    await authService.login(_email, _password);
    if (!mounted) return; // ← guardia antes de usar context
    context.go('/home');
  } on AuthException catch (e) {
    if (!mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.message)));
  } finally {
    if (mounted) setState(() => _isLoading = false);
  }
}

4. Arquitectura de Widgets

Extraer a Clases, No a Métodos

// MALO — método privado que devuelve un widget, impide la optimización
Widget _buildHeader() {
  return Container(
    padding: const EdgeInsets.all(16),
    child: Text(title, style: Theme.of(context).textTheme.headlineMedium),
  );
}

// BUENO — clase de widget separada, permite const, reutilización de elementos
class _PageHeader extends StatelessWidget {
  const _PageHeader(this.title);
  final String title;

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(16),
      child: Text(title, style: Theme.of(context).textTheme.headlineMedium),
    );
  }
}

Propagación de const

// MALO — nuevas instancias en cada reconstrucción
child: Padding(
  padding: EdgeInsets.all(16.0),       // no es const
  child: Icon(Icons.home, size: 24.0), // no es const
)

// BUENO — const detiene la propagación de reconstrucciones
child: const Padding(
  padding: EdgeInsets.all(16.0),
  child: Icon(Icons.home, size: 24.0),
)

Reconstrucciones Acotadas

// MALO — toda la página se reconstruye en cada cambio del contador
class CounterPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider); // reconstruye todo
    return Scaffold(
      body: Column(children: [
        const ExpensiveHeader(), // reconstruido innecesariamente
        Text('$count'),
        const ExpensiveFooter(), // reconstruido innecesariamente
      ]),
    );
  }
}

// BUENO — aislar la parte que se reconstruye
class CounterPage extends StatelessWidget {
  const CounterPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Column(children: [
        ExpensiveHeader(),        // nunca se reconstruye (const)
        _CounterDisplay(),        // solo esto se reconstruye
        ExpensiveFooter(),        // nunca se reconstruye (const)
      ]),
    );
  }
}

class _CounterDisplay extends ConsumerWidget {
  const _CounterDisplay();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Text('$count');
  }
}

5. Gestión de Estado: BLoC/Cubit

// Cubit — estado síncrono o asíncrono simple
class AuthCubit extends Cubit<AuthState> {
  AuthCubit(this._authService) : super(const AuthState.initial());
  final AuthService _authService;

  Future<void> login(String email, String password) async {
    emit(const AuthState.loading());
    try {
      final user = await _authService.login(email, password);
      emit(AuthState.authenticated(user));
    } on AuthException catch (e) {
      emit(AuthState.error(e.message));
    }
  }

  void logout() {
    _authService.logout();
    emit(const AuthState.initial());
  }
}

// En el widget
BlocBuilder<AuthCubit, AuthState>(
  builder: (context, state) => switch (state) {
    AuthInitial() => const LoginForm(),
    AuthLoading() => const CircularProgressIndicator(),
    AuthAuthenticated(:final user) => HomePage(user: user),
    AuthError(:final message) => ErrorView(message: message),
  },
)

6. Gestión de Estado: Riverpod

// Provider asíncrono con auto-descarte
@riverpod
Future<List<Product>> products(Ref ref) async {
  final repo = ref.watch(productRepositoryProvider);
  return repo.getAll();
}

// Notifier con mutaciones complejas
@riverpod
class CartNotifier extends _$CartNotifier {
  @override
  List<CartItem> build() => [];

  void add(Product product) {
    final existing = state.where((i) => i.productId == product.id).firstOrNull;
    if (existing != null) {
      state = [
        for (final item in state)
          if (item.productId == product.id) item.copyWith(quantity: item.quantity + 1)
          else item,
      ];
    } else {
      state = [...state, CartItem(productId: product.id, quantity: 1)];
    }
  }

  void remove(String productId) =>
      state = state.where((i) => i.productId != productId).toList();

  void clear() => state = [];
}

// Provider derivado (patrón selector)
@riverpod
int cartCount(Ref ref) => ref.watch(cartNotifierProvider).length;

@riverpod
double cartTotal(Ref ref) {
  final cart = ref.watch(cartNotifierProvider);
  final products = ref.watch(productsProvider).valueOrNull ?? [];
  return cart.fold(0.0, (total, item) {
    // firstWhereOrNull (del paquete collection) evita StateError cuando falta un producto
    final product = products.firstWhereOrNull((p) => p.id == item.productId);
    return total + (product?.price ?? 0) * item.quantity;
  });
}

7. Navegación con GoRouter

final router = GoRouter(
  initialLocation: '/',
  // refreshListenable reevalúa la redirección cada vez que cambia el estado de autenticación
  refreshListenable: GoRouterRefreshStream(authCubit.stream),
  redirect: (context, state) {
    final isLoggedIn = context.read<AuthCubit>().state is AuthAuthenticated;
    final isGoingToLogin = state.matchedLocation == '/login';
    if (!isLoggedIn && !isGoingToLogin) return '/login';
    if (isLoggedIn && isGoingToLogin) return '/';
    return null;
  },
  routes: [
    GoRoute(path: '/login', builder: (_, __) => const LoginPage()),
    ShellRoute(
      builder: (context, state, child) => AppShell(child: child),
      routes: [
        GoRoute(path: '/', builder: (_, __) => const HomePage()),
        GoRoute(
          path: '/products/:id',
          builder: (context, state) =>
              ProductDetailPage(id: state.pathParameters['id']!),
        ),
      ],
    ),
  ],
);

8. HTTP con Dio

final dio = Dio(BaseOptions(
  baseUrl: const String.fromEnvironment('API_URL'),
  connectTimeout: const Duration(seconds: 10),
  receiveTimeout: const Duration(seconds: 30),
  headers: {'Content-Type': 'application/json'},
));

// Añadir interceptor de autenticación
dio.interceptors.add(InterceptorsWrapper(
  onRequest: (options, handler) async {
    final token = await secureStorage.read(key: 'auth_token');
    if (token != null) options.headers['Authorization'] = 'Bearer $token';
    handler.next(options);
  },
  onError: (error, handler) async {
    // Proteger contra bucles infinitos de reintento: solo intentar refrescar una vez por solicitud
    final isRetry = error.requestOptions.extra['_isRetry'] == true;
    if (!isRetry && error.response?.statusCode == 401) {
      final refreshed = await attemptTokenRefresh();
      if (refreshed) {
        error.requestOptions.extra['_isRetry'] = true;
        return handler.resolve(await dio.fetch(error.requestOptions));
      }
    }
    handler.next(error);
  },
));

// Repositorio usando Dio
class UserApiDataSource {
  const UserApiDataSource(this._dio);
  final Dio _dio;

  Future<User> getById(String id) async {
    final response = await _dio.get<Map<String, dynamic>>('/users/$id');
    return User.fromJson(response.data!);
  }
}

9. Arquitectura de Manejo de Errores

// Captura global de errores — configurar en main()
void main() {
  FlutterError.onError = (details) {
    FlutterError.presentError(details);
    crashlytics.recordFlutterFatalError(details);
  };

  PlatformDispatcher.instance.onError = (error, stack) {
    crashlytics.recordError(error, stack, fatal: true);
    return true;
  };

  runApp(const App());
}

// ErrorWidget personalizado para producción
class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: router,
      builder: (context, widget) {
        ErrorWidget.builder = (FlutterErrorDetails details) => Material(
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Icon(Icons.error_outline, size: 48, color: Colors.red),
                const SizedBox(height: 16),
                const Text('Algo salió mal'),
                const SizedBox(height: 8),
                Text(details.exception.toString(),
                    style: const TextStyle(fontSize: 12)),
                const SizedBox(height: 16),
                ElevatedButton(
                  onPressed: () => context.go('/'),
                  child: const Text('Reintentar'),
                ),
              ],
            ),
          ),
        );
        return widget!;
      },
    );
  }
}

10. Principios de Testing

Tests Unitarios para Cubits

void main() {
  group('AuthCubit', () {
    late MockAuthService authService;
    late AuthCubit cubit;

    setUp(() {
      authService = MockAuthService();
      cubit = AuthCubit(authService);
    });

    test('emite [loading, authenticated] cuando el login es exitoso', () async {
      when(authService.login(any, any)).thenAnswer((_) async => User(id: '1', name: 'Test'));

      expect(
        cubit.stream,
        emitsInOrder([
          const AuthState.loading(),
          isA<AuthAuthenticated>(),
        ]),
      );

      await cubit.login('[email protected]', 'password');
    });

    test('emite [loading, error] cuando el login falla', () async {
      when(authService.login(any, any)).thenThrow(AuthException('Credenciales inválidas'));

      expect(
        cubit.stream,
        emitsInOrder([
          const AuthState.loading(),
          const AuthState.error('Credenciales inválidas'),
        ]),
      );

      await cubit.login('[email protected]', 'wrong_password');
    });
  });
}

Tests de Widgets con Anulaciones de ProviderScope

testWidgets('muestra lista de productos', (tester) async {
  final products = [
    Product(id: '1', name: 'Widget A', price: 9.99),
    Product(id: '2', name: 'Widget B', price: 14.99),
  ];

  await tester.pumpWidget(
    ProviderScope(
      overrides: [
        productsProvider.overrideWith((ref) => Future.value(products)),
        cartNotifierProvider.overrideWith((ref) => CartNotifier()),
      ],
      child: const MaterialApp(home: ProductListPage()),
    ),
  );

  expect(find.text('Widget A'), findsOneWidget);
  expect(find.text('Widget B'), findsOneWidget);
});

Preferir Fakes sobre Mocks

// Fake en lugar de Mock — mantiene el comportamiento real de las colecciones
class FakeCartRepository extends Fake implements CartRepository {
  final _items = <CartItem>[];

  void addItem(CartItem item) => _items.add(item);
  void removeItem(String productId) => _items.removeWhere((i) => i.productId == productId);

  @override
  List<CartItem> getItems() => List.unmodifiable(_items);

  @override
  void clear() => _items.clear();
}

// Uso en test
final repo = FakeCartRepository();
repo.addItem(CartItem(productId: '1', quantity: 2));

final result = repo.getItems();
expect(result.length, 1);
expect(result.first.quantity, 2);

// El fake es verificable y no requiere configuraciones de when(), etc.

11. Almacenamiento Local Seguro

// Usando flutter_secure_storage para tokens y datos sensibles
class SecureStorage {
  const SecureStorage(this._storage);
  final FlutterSecureStorage _storage;

  static const _tokenKey = 'auth_token';
  static const _refreshTokenKey = 'refresh_token';

  Future<void> saveTokens(AuthTokens tokens) async {
    await Future.wait([
      _storage.write(key: _tokenKey, value: tokens.accessToken),
      _storage.write(key: _refreshTokenKey, value: tokens.refreshToken),
    ]);
  }

  Future<String?> getAccessToken() => _storage.read(key: _tokenKey);
  Future<String?> getRefreshToken() => _storage.read(key: _refreshTokenKey);

  Future<void> clearAll() async {
    await Future.wait([
      _storage.delete(key: _tokenKey),
      _storage.delete(key: _refreshTokenKey),
    ]);
  }
}

// Usando shared_preferences para preferencias no sensibles
class UserPreferences {
  const UserPreferences(this._prefs);
  final SharedPreferences _prefs;

  bool get isDarkMode => _prefs.getBool('dark_mode') ?? false;
  set isDarkMode(bool value) => _prefs.setBool('dark_mode', value);

  String? get lastOpenedProductId => _prefs.getString('last_product_id');
  set lastOpenedProductId(String? value) {
    if (value != null) {
      _prefs.setString('last_product_id', value);
    } else {
      _prefs.remove('last_product_id');
    }
  }
}

12. Arquitectura Limpia (Carpetas y Responsabilidades)

Estructura de proyecto recomendada para aplicaciones de tamaño medio-grande:

lib/
├── main.dart
├── app.dart
├── core/
│   ├── constants/        # Rutas de API, claves, valores fijos
│   ├── enums/            # Enumeraciones compartidas
│   ├── errors/           # Excepciones, fallos
│   ├── network/          # Cliente Dio configurado, interceptores
│   ├── router/           # Configuración de GoRouter
│   ├── theme/            # Tema de Material
│   └── utils/            # Extensiones, helpers, formatos
├── data/
│   ├── datasources/      # Fuentes de datos (remotas, locales)
│   ├── dtos/             # Objetos de transferencia de datos (fromJson/toJson)
│   └── repositories/     # Implementaciones de repositorios
├── domain/
│   ├── entities/         # Entidades puras de Dart (sin dependencias externas)
│   ├── repositories/     # Interfaces/contratos abstractos de repositorios
│   └── usecases/         # Casos de uso (orquestan la lógica de negocio)
└── presentation/
    ├── providers/        # StateNotifiers, ChangeNotifiers, Riverpod providers
    ├── pages/            # Páginas enteras (con Scaffold)
    ├── widgets/          # Widgets reutilizables
    └── shared/           # Widgets y lógica de presentación compartida

Contratos de Repositorio (Capa de Dominio)

// Capa de dominio — sin importaciones de framework
abstract class UserRepository {
  Future<User> getById(String id);
  Future<List<User>> getAll();
  Future<User> create(CreateUserParams params);
  Future<User> update(UpdateUserParams params);
  Future<void> delete(String id);
  Stream<User?> watchById(String id);
}

Caso de Uso de Ejemplo

class GetUserByIdUseCase {
  const GetUserByIdUseCase(this._repository);
  final UserRepository _repository;

  Future<Result<User>> execute(String id) async {
    if (id.isEmpty) {
      return const Result.failure(ValidationError('ID no puede estar vacío'));
    }
    try {
      final user = await _repository.getById(id);
      return Result.success(user);
    } on RepositoryException catch (e) {
      return Result.failure(e);
    }
  }
}

Patrones prácticos y listos para producción para aplicaciones Dart y Flutter. Agnóstico en cuanto a librerías cuando es posible, con cobertura explícita de los paquetes más comunes del ecosistema.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Skills relacionados