Agent skill

Dart

Execute these commands after EVERY implementation (see AGENT_AUTOMATION module for full workflow).

Stars 10
Forks 1

Install this agent skill to your Project

npx add-skill https://github.com/hivellm/rulebook/tree/main/templates/skills/languages/dart

SKILL.md

Dart Project Rules

Agent Automation Commands

CRITICAL: Execute these commands after EVERY implementation (see AGENT_AUTOMATION module for full workflow).

bash
# Complete quality check sequence:
dart format --set-exit-if-changed .  # Format check
dart analyze --fatal-infos  # Linting + analysis
dart test                   # All tests (100% pass)
dart test --coverage=coverage  # Coverage check

# Security audit:
dart pub outdated           # Check outdated deps

Dart Configuration

CRITICAL: Use Dart 3.0+ with sound null safety and strict analysis.

  • Version: Dart 3.0+
  • Recommended: Dart 3.3+
  • Null Safety: Sound null safety required
  • Analysis: dart analyze with strict mode
  • Testing: package:test

pubspec.yaml Requirements

yaml
name: your_package
description: A comprehensive package description
version: 0.1.0
homepage: https://github.com/you/your_package

environment:
  sdk: '>=3.0.0 <4.0.0'

dependencies:
  http: ^1.1.0
  
dev_dependencies:
  test: ^1.24.0
  lints: ^3.0.0
  coverage: ^1.7.0
  dart_code_metrics: ^5.7.0

analysis_options.yaml Requirements

yaml
include: package:lints/recommended.yaml

analyzer:
  language:
    strict-casts: true
    strict-inference: true
    strict-raw-types: true
  
  errors:
    invalid_annotation_target: error
    missing_required_param: error
    missing_return: error
  
  exclude:
    - build/**
    - .dart_tool/**

linter:
  rules:
    - always_declare_return_types
    - always_require_non_null_named_parameters
    - avoid_print
    - avoid_relative_lib_imports
    - avoid_returning_null_for_void
    - avoid_slow_async_io
    - avoid_types_as_parameter_names
    - cancel_subscriptions
    - close_sinks
    - prefer_const_constructors
    - prefer_final_fields
    - prefer_final_locals
    - sort_constructors_first
    - unawaited_futures

Code Quality Standards

Mandatory Quality Checks

CRITICAL: After implementing ANY feature, you MUST run these commands in order.

IMPORTANT: These commands MUST match your GitHub Actions workflows to prevent CI/CD failures!

bash
# Pre-Commit Checklist (MUST match .github/workflows/*.yml)

# 1. Format check (matches workflow - use --set-exit-if-changed!)
dart format --set-exit-if-changed --line-length 100 .

# 2. Analyze (MUST pass with no issues - matches workflow)
dart analyze --fatal-infos --fatal-warnings

# 3. Run all tests (MUST pass 100% - matches workflow)
dart test

# 4. Check coverage (MUST meet threshold - matches workflow)
dart test --coverage=coverage
dart pub global activate coverage
dart pub global run coverage:format_coverage \
  --lcov --in=coverage --out=coverage/lcov.info --report-on=lib

# 5. Metrics check (matches workflow)
dart pub global activate dart_code_metrics
dart pub global run dart_code_metrics:metrics analyze lib

# If ANY fails: ❌ DO NOT COMMIT - Fix first!

If ANY of these fail, you MUST fix the issues before committing.

Why This Matters:

  • Running different commands locally than in CI causes "works on my machine" failures
  • CI/CD workflows will fail if commands don't match
  • Example: Using dart format . locally but dart format --set-exit-if-changed in CI = failure
  • Example: Missing --fatal-infos flag = warnings pass locally but fail in CI

Testing

  • Framework: package:test
  • Location: /test directory
  • Coverage: package:coverage (80%+ threshold)
  • Naming: test files must end with _test.dart

Example test structure:

dart
import 'package:test/test.dart';
import 'package:your_package/your_package.dart';

void main() {
  group('DataProcessor', () {
    late DataProcessor processor;
    
    setUp(() {
      processor = DataProcessor(threshold: 0.5);
    });
    
    tearDown(() {
      // Cleanup
    });
    
    test('processes valid input correctly', () {
      final input = [1, 2, 3, 4, 5];
      final result = processor.process(input);
      
      expect(result, isNotEmpty);
      expect(result.length, greaterThan(0));
    });
    
    test('handles empty input', () {
      final result = processor.process([]);
      expect(result, isEmpty);
    });
    
    test('throws on null input', () {
      expect(
        () => processor.process(null as List<int>),
        throwsA(isA<ArgumentError>()),
      );
    });
  });
  
  group('Validator', () {
    test('validates correct data', () {
      expect(Validator.isValid('test'), isTrue);
    });
    
    test('rejects invalid data', () {
      expect(Validator.isValid(''), isFalse);
    });
  });
}

Null Safety

  • REQUIRED: Sound null safety for all code
  • Use ? for nullable types
  • Use ! operator sparingly
  • Prefer null-aware operators

Example:

dart
// ✅ GOOD: Proper null safety
class User {
  final String id;
  final String name;
  final String? email;  // Nullable
  final int? age;       // Nullable
  
  User({
    required this.id,
    required this.name,
    this.email,
    this.age,
  });
  
  String getDisplayName() {
    return email != null ? '$name <$email>' : name;
  }
  
  int getAgeOrDefault() => age ?? 0;
}

// ❌ BAD: Unsafe null handling
class User {
  String id;
  String name;
  String email;  // Should be nullable but isn't
  
  User(this.id, this.name, this.email);
  
  String getDisplayName() {
    return '$name <$email>';  // Crash if email is null!
  }
}

Async Programming

  • Always use async/await
  • Handle Future errors with try/catch
  • Use unawaited() to mark intentionally unawaited futures

Example:

dart
// ✅ GOOD: Proper async handling
Future<List<User>> fetchUsers() async {
  try {
    final response = await http.get(Uri.parse('https://api.example.com/users'));
    
    if (response.statusCode != 200) {
      throw HttpException('Failed to load users: ${response.statusCode}');
    }
    
    final List<dynamic> data = jsonDecode(response.body);
    return data.map((json) => User.fromJson(json)).toList();
  } catch (e) {
    print('Error fetching users: $e');
    rethrow;
  }
}

// ❌ BAD: No error handling
Future<List<User>> fetchUsers() async {
  final response = await http.get(Uri.parse('https://api.example.com/users'));
  final List<dynamic> data = jsonDecode(response.body);  // Could crash!
  return data.map((json) => User.fromJson(json)).toList();
}

Best Practices

DO's ✅

  • USE sound null safety
  • USE const constructors when possible
  • PREFER final for variables
  • DOCUMENT public APIs
  • HANDLE errors explicitly
  • TEST all code paths
  • USE meaningful names
  • FOLLOW dart_style guidelines

DON'Ts ❌

  • NEVER use dynamic unless absolutely necessary
  • NEVER suppress analyzer warnings without justification
  • NEVER use print() in production code (use logging)
  • NEVER ignore lint warnings
  • NEVER use deprecated APIs
  • NEVER commit with failing tests
  • NEVER use ! operator without null check

CI/CD Requirements

Must include GitHub Actions workflows:

  1. Testing (dart-test.yml):

    • Test on ubuntu-latest, windows-latest, macos-latest
    • Dart versions: stable, beta
    • Upload coverage
  2. Linting (dart-lint.yml):

    • dart format --set-exit-if-changed
    • dart analyze --fatal-infos --fatal-warnings
  3. Build (dart-build.yml):

    • dart compile exe (for CLI apps)
    • Verify output artifacts

Publishing to pub.dev

Prerequisites

  1. Create account at https://pub.dev
  2. Run dart pub login
  3. Verify package name available

Publishing Workflow

bash
# 1. Update version in pubspec.yaml
# 2. Update CHANGELOG.md
# 3. Run all quality checks
dart format --set-exit-if-changed .
dart analyze --fatal-infos --fatal-warnings
dart test

# 4. Dry run
dart pub publish --dry-run

# 5. Publish
dart pub publish

# 6. Create git tag
git tag -a v1.0.0 -m "Release 1.0.0"

Didn't find tool you were looking for?

Be as detailed as possible for better results