Token导航 LogoToken导航

Agent Skill · 开发

core-layer-patterns(核心层图案)

core-layer-patterns 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

core-layer-patterns 是开发类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证为 MIT,第三方扫描已通过,按安装量在开发类的 21,746 个 Skill 里排第 3,255。

总安装 12,140周安装 401GitHub Stars 8下载 3,223核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:core-layer-patterns(核心层图案)
来源仓库:https://github.com/kaakati/rails-enterprise-dev
仓库路径:skills/core-layer-patterns
安装命令:
npx skills add https://github.com/kaakati/rails-enterprise-dev --skill 'Core Layer Patterns'
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/kaakati/rails-enterprise-dev --skill 'Core Layer Patterns'

这个 Skill 是做什么的

开发类 Skill 面向写代码、改代码和排查问题,一般会读写仓库文件,部分还会执行构建或测试命令。

提供 Clean Architecture 中核心层的标准实现模式与目录结构。适用于 Flutter 项目的错误处理、依赖注入与配置管理规范化。包含 Failure/Exception 基类、Dart 扩展与主题配置的模板代码。

安装方式:GitHub,命令为 npx skills add https://github.com/kaakati/rails-enterprise-dev --skill 'Core Layer Patterns'。core-layer-patterns 属于开发类 Skill,可作为该场景下的辅助能力补充。

来源仓库内路径:skills/core-layer-patterns

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/kaakati/rails-enterprise-dev --skill 'Core Lay…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

core-layer-patterns 怎么安装?

在终端执行 npx skills add https://github.com/kaakati/rails-enterprise-dev --skill 'Core Layer Pattern…。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

core-layer-patterns 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

core-layer-patterns 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

core-layer-patterns 是免费的吗?

来源标注的许可证是 MIT。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

core-layer-patterns 用的人多吗?

公开数据是累计安装 12,140 次,近一周安装 401 次,下载 3,223 次,GitHub 8 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

core-layer-patterns 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 3 个目录同时收录,说明这个 Skill 流传比较广。
  • 许可证明确为 MIT,商用和分发边界好判断。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理开发类任务、又不想每次都手写提示词的人。
  • 需要许可证清楚、并且有第三方扫描结果的团队项目。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

开发类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
skill-vetter

技能审查员 · OpenClaw

8,779,018986操作浏览器MIT-0通过
ontology

本体论 · OpenClaw

6,636,960576只读MIT-0通过
entra-app-registration

内部应用程序注册 · Codex、Claude

6,631,872764接触敏感数据未明确通过
entra-app-registration

内部应用程序注册 · Codex、Claude

3,757,680201接触敏感数据未明确通过
nano-banana-pro

纳米香蕉专业版 · OpenClaw

3,526,898364接触敏感数据MIT-0有提醒
core-layer-patterns

核心层图案 · 本页

12,1408需要联网MIT通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Core Layer Patterns

The core layer provides fundamental building blocks used across all other layers in Clean Architecture. It contains no Flutter-specific code and focuses on pure Dart patterns.

Directory Structure

lib/core/
├── errors/
│   ├── failures.dart         # Base Failure classes
│   └── exceptions.dart       # Base Exception classes
├── utils/
│   ├── extensions.dart       # Dart extensions
│   └── validators.dart       # Input validators
├── config/
│   ├── app_config.dart       # Environment configuration
│   └── theme_config.dart     # Theme configuration
└── di/
    └── injection_container.dart  # Dependency injection setup

Error Handling Patterns

Failures (Domain Layer)

Failures represent expected error states in the domain layer. They are returned from use cases using the Either<Failure, T> pattern.

// lib/core/errors/failures.dart
abstract class Failure {
  final String message;
  const Failure(this.message);

  @override
  String toString() => message;

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is Failure &&
          runtimeType == other.runtimeType &&
          message == other.message;

  @override
  int get hashCode => message.hashCode;
}

// Network-related failures
class ServerFailure extends Failure {
  const ServerFailure([String message = 'Server error occurred']) : super(message);
}

class NetworkFailure extends Failure {
  const NetworkFailure([String message = 'Network error occurred']) : super(message);
}

class TimeoutFailure extends Failure {
  const TimeoutFailure([String message = 'Request timed out']) : super(message);
}

// Data-related failures
class CacheFailure extends Failure {
  const CacheFailure([String message = 'Cache error occurred']) : super(message);
}

class ParseFailure extends Failure {
  const ParseFailure([String message = 'Failed to parse data']) : super(message);
}

// Validation failures
class ValidationFailure extends Failure {
  const ValidationFailure([String message = 'Validation error occurred']) : super(message);
}

class InvalidInputFailure extends Failure {
  const InvalidInputFailure([String message = 'Invalid input provided']) : super(message);
}

// Authentication failures
class UnauthorizedFailure extends Failure {
  const UnauthorizedFailure([String message = 'Unauthorized access']) : super(message);
}

class ForbiddenFailure extends Failure {
  const ForbiddenFailure([String message = 'Access forbidden']) : super(message);
}

// Not found failures
class NotFoundFailure extends Failure {
  const NotFoundFailure([String message = 'Resource not found']) : super(message);
}

Usage in Use Cases:

class LoginUser {
  final UserRepository repository;
  const LoginUser({required this.repository});

  Future<Either<Failure, User>> call(String email, String password) async {
    return await repository.login(email, password);
  }
}

Exceptions (Data Layer)

Exceptions represent unexpected error states in the data layer. They are thrown by data sources and caught by repositories.

// lib/core/errors/exceptions.dart
class ServerException implements Exception {
  final String message;
  final int? statusCode;

  const ServerException(this.message, [this.statusCode]);

  @override
  String toString() => 'ServerException: $message (status: $statusCode)';
}

class NetworkException implements Exception {
  final String message;

  const NetworkException(this.message);

  @override
  String toString() => 'NetworkException: $message';
}

class CacheException implements Exception {
  final String message;

  const CacheException(this.message);

  @override
  String toString() => 'CacheException: $message';
}

class ParseException implements Exception {
  final String message;
  final dynamic originalError;

  const ParseException(this.message, [this.originalError]);

  @override
  String toString() => 'ParseException: $message';
}

class UnauthorizedException implements Exception {
  final String message;

  const UnauthorizedException([this.message = 'Unauthorized']);

  @override
  String toString() => 'UnauthorizedException: $message';
}

Usage in Repositories:

class UserRepositoryImpl implements UserRepository {
  @override
  Future<Either<Failure, User>> login(String email, String password) async {
    try {
      final userModel = await remoteDataSource.login(email, password);
      return Right(userModel.toEntity());
    } on ServerException catch (e) {
      return Left(ServerFailure(e.message));
    } on NetworkException catch (e) {
      return Left(NetworkFailure(e.message));
    } on UnauthorizedException catch (e) {
      return Left(UnauthorizedFailure(e.message));
    } catch (e) {
      return Left(ServerFailure('Unexpected error: $e'));
    }
  }
}

Extension Methods

// lib/core/utils/extensions.dart
import 'package:flutter/material.dart';

/// String extensions
extension StringExtensions on String {
  /// Capitalize first letter
  String capitalize() {
    if (isEmpty) return this;
    return '${this[0].toUpperCase()}${substring(1)}';
  }

  /// Check if string is valid email
  bool get isValidEmail {
    final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
    return emailRegex.hasMatch(this);
  }

  /// Check if string is valid phone
  bool get isValidPhone {
    final phoneRegex = RegExp(r'^\+?[\d\s-]{10,}$');
    return phoneRegex.hasMatch(this);
  }

  /// Remove all whitespace
  String removeWhitespace() => replaceAll(RegExp(r'\s+'), '');
}

/// DateTime extensions
extension DateTimeExtensions on DateTime {
  /// Check if date is today
  bool get isToday {
    final now = DateTime.now();
    return year == now.year && month == now.month && day == now.day;
  }

  /// Check if date is yesterday
  bool get isYesterday {
    final yesterday = DateTime.now().subtract(const Duration(days: 1));
    return year == yesterday.year &&
        month == yesterday.month &&
        day == yesterday.day;
  }

  /// Format as relative time (2 hours ago, 3 days ago)
  String get relativeTime {
    final now = DateTime.now();
    final difference = now.difference(this);

    if (difference.inDays > 365) {
      return '${(difference.inDays / 365).floor()} year${difference.inDays > 730 ? 's' : ''} ago';
    } else if (difference.inDays > 30) {
      return '${(difference.inDays / 30).floor()} month${difference.inDays > 60 ? 's' : ''} ago';
    } else if (difference.inDays > 0) {
      return '${difference.inDays} day${difference.inDays > 1 ? 's' : ''} ago';
    } else if (difference.inHours > 0) {
      return '${difference.inHours} hour${difference.inHours > 1 ? 's' : ''} ago';
    } else if (difference.inMinutes > 0) {
      return '${difference.inMinutes} minute${difference.inMinutes > 1 ? 's' : ''} ago';
    } else {
      return 'Just now';
    }
  }
}

/// List extensions
extension ListExtensions<T> on List<T> {
  /// Get element at index or null
  T? elementAtOrNull(int index) {
    if (index < 0 || index >= length) return null;
    return this[index];
  }

  /// Remove duplicates
  List<T> unique() => toSet().toList();
}

/// BuildContext extensions
extension ContextExtensions on BuildContext {
  /// Get screen width
  double get screenWidth => MediaQuery.of(this).size.width;

  /// Get screen height
  double get screenHeight => MediaQuery.of(this).size.height;

  /// Get theme
  ThemeData get theme => Theme.of(this);

  /// Get text theme
  TextTheme get textTheme => Theme.of(this).textTheme;

  /// Show snackbar
  void showSnackBar(String message, {bool isError = false}) {
    ScaffoldMessenger.of(this).showSnackBar(
      SnackBar(
        content: Text(message),
        backgroundColor: isError ? Colors.red : null,
      ),
    );
  }
}

Validators

// lib/core/utils/validators.dart
class Validators {
  /// Email validator
  static String? email(String? value) {
    if (value == null || value.isEmpty) {
      return 'Email is required';
    }
    if (!value.isValidEmail) {
      return 'Please enter a valid email';
    }
    return null;
  }

  /// Password validator
  static String? password(String? value, {int minLength = 8}) {
    if (value == null || value.isEmpty) {
      return 'Password is required';
    }
    if (value.length < minLength) {
      return 'Password must be at least $minLength characters';
    }
    return null;
  }

  /// Phone validator
  static String? phone(String? value) {
    if (value == null || value.isEmpty) {
      return 'Phone number is required';
    }
    if (!value.isValidPhone) {
      return 'Please enter a valid phone number';
    }
    return null;
  }

  /// Required field validator
  static String? required(String? value, {String? fieldName}) {
    if (value == null || value.trim().isEmpty) {
      return '${fieldName ?? 'This field'} is required';
    }
    return null;
  }

  /// Min length validator
  static String? minLength(String? value, int min, {String? fieldName}) {
    if (value == null || value.length < min) {
      return '${fieldName ?? 'This field'} must be at least $min characters';
    }
    return null;
  }

  /// Max length validator
  static String? maxLength(String? value, int max, {String? fieldName}) {
    if (value != null && value.length > max) {
      return '${fieldName ?? 'This field'} must not exceed $max characters';
    }
    return null;
  }

  /// Compose multiple validators
  static String? Function(String?) compose(List<String? Function(String?)> validators) {
    return (value) {
      for (final validator in validators) {
        final error = validator(value);
        if (error != null) return error;
      }
      return null;
    };
  }
}

Usage in Forms:

TextFormField(
  validator: Validators.compose([
    Validators.required,
    Validators.email,
  ]),
  decoration: const InputDecoration(labelText: 'Email'),
)

Application Configuration

// lib/core/config/app_config.dart
class AppConfig {
  final String appName;
  final String apiBaseUrl;
  final String apiKey;
  final int connectTimeout;
  final int receiveTimeout;
  final bool enableLogging;

  const AppConfig({
    required this.appName,
    required this.apiBaseUrl,
    required this.apiKey,
    this.connectTimeout = 30000,
    this.receiveTimeout = 30000,
    this.enableLogging = false,
  });

  /// Development configuration
  factory AppConfig.development() {
    return const AppConfig(
      appName: 'MyApp (Dev)',
      apiBaseUrl: 'https://dev-api.example.com',
      apiKey: 'dev_api_key',
      enableLogging: true,
    );
  }

  /// Staging configuration
  factory AppConfig.staging() {
    return const AppConfig(
      appName: 'MyApp (Staging)',
      apiBaseUrl: 'https://staging-api.example.com',
      apiKey: 'staging_api_key',
      enableLogging: true,
    );
  }

  /// Production configuration
  factory AppConfig.production() {
    return const AppConfig(
      appName: 'MyApp',
      apiBaseUrl: 'https://api.example.com',
      apiKey: 'prod_api_key',
      enableLogging: false,
    );
  }
}

Theme Configuration

// lib/core/config/theme_config.dart
import 'package:flutter/material.dart';

class ThemeConfig {
  /// Light theme
  static ThemeData lightTheme() {
    return ThemeData(
      useMaterial3: true,
      colorScheme: ColorScheme.fromSeed(
        seedColor: Colors.blue,
        brightness: Brightness.light,
      ),
      appBarTheme: const AppBarTheme(
        elevation: 0,
        centerTitle: true,
      ),
      cardTheme: CardTheme(
        elevation: 2,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(12),
        ),
      ),
      inputDecorationTheme: InputDecorationTheme(
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(8),
        ),
        filled: true,
      ),
    );
  }

  /// Dark theme
  static ThemeData darkTheme() {
    return ThemeData(
      useMaterial3: true,
      colorScheme: ColorScheme.fromSeed(
        seedColor: Colors.blue,
        brightness: Brightness.dark,
      ),
      appBarTheme: const AppBarTheme(
        elevation: 0,
        centerTitle: true,
      ),
      cardTheme: CardTheme(
        elevation: 2,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(12),
        ),
      ),
      inputDecorationTheme: InputDecorationTheme(
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(8),
        ),
        filled: true,
      ),
    );
  }
}

Dependency Injection

// lib/core/di/injection_container.dart
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:http/http.dart' as http;

class DependencyInjection {
  /// Initialize all dependencies
  static Future<void> init() async {
    // Core dependencies
    _initCore();

    // Data sources
    _initDataSources();

    // Repositories
    _initRepositories();

    // Use cases
    _initUseCases();
  }

  static void _initCore() {
    // HTTP client
    Get.put<http.Client>(http.Client(), permanent: true);

    // GetStorage
    Get.put<GetStorage>(GetStorage(), permanent: true);

    // App configuration
    Get.put<AppConfig>(AppConfig.production(), permanent: true);
  }

  static void _initDataSources() {
    // Register data sources
    // Example:
    // Get.lazyPut<UserRemoteDataSource>(
    //   () => UserRemoteDataSourceImpl(http: Get.find()),
    // );
  }

  static void _initRepositories() {
    // Register repositories
    // Example:
    // Get.lazyPut<UserRepository>(
    //   () => UserRepositoryImpl(
    //     remoteDataSource: Get.find(),
    //     localDataSource: Get.find(),
    //   ),
    // );
  }

  static void _initUseCases() {
    // Register use cases
    // Example:
    // Get.lazyPut(() => LoginUser(repository: Get.find()));
  }
}

Usage in main.dart:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await GetStorage.init();
  await DependencyInjection.init();
  runApp(const MyApp());
}

Best Practices

  1. Failures vs Exceptions:

- Use Failure in domain layer (returned via Either) - Use Exception in data layer (thrown and caught) - Never throw exceptions from use cases

  1. Extension Methods:

- Keep extensions focused and single-purpose - Avoid overly generic extension names - Document complex extensions

  1. Configuration:

- Use factory constructors for different environments - Never hardcode sensitive data (API keys, secrets) - Use environment variables for sensitive config

  1. Dependency Injection:

- Register dependencies in correct order (data sources → repositories → use cases → controllers) - Use lazyPut for most dependencies - Use put with permanent: true for singletons needed throughout app lifecycle

平台分布

Claude Code

33%
按下载量换算1,064

windsurf

22.19%
按下载量换算715

OpenCode

20.09%
按下载量换算648

Codex

11.57%
按下载量换算373

Antigravity

8.83%
按下载量换算285

Gemini CLI

3.29%
按下载量换算106

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

继续浏览同类 Skills