Create infrastructure
Skill anoopsg/agent_rules/src/exclusive/skills/create-infrastructure
Behavioral blueprints for agents.
npx -y skills add anoopsg/agent_rules --skill create-infrastructureAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Creates new domains in the infrastructure layer using dart_mappable for models, Chopper for networking, and Riverpod for repositories. Use when the user asks to create a repository, API service, data model, or set up new infrastructure components.
SKILL.md
4.0 KB, as published. Nobody here has run it
Infrastructure Creation Skill
This skill defines the process for creating a new domain in the
infrastructure/ layer, covering models, networking, and repositories.
1. Directory Structure
Infrastructure is organized by domain in lib/src/infrastructure/:
lib/src/infrastructure/my_domain/
├── models/ # DTOs and Domain Models
│ └── user_model.dart # Uses dart_mappable
├── services/ # Remote API definitions (Chopper)
│ └── my_service.dart
├── my_repository.dart # Business logic interface
└── my_domain.dart # Barrel file
2. Data Modeling (dart_mappable)
Always use @MappableClass() for models to get built-in JSON conversion
and equality. Models must be immutable.
@MappableClass()
class UserProfile with UserProfileMappable {
const UserProfile({
required this.id,
required this.email,
});
final String id;
final String email;
}
3. Networking (Chopper)
Define API services as abstract classes extending ChopperService. Use
apiClient to consume them.
3.1 Define the Service
@ChopperApi()
abstract class MyService extends ChopperService {
static MyService create([ChopperClient? client]) => _$MyService(client);
@GET(path: '/profile')
Future<Response<UserProfile>> getProfile();
}
3.2 Register the Service
Add the service to apiClient in lib/src/infrastructure/_clients/api_client.dart:
@Riverpod(keepAlive: true)
ApiClient apiClient(Ref ref) {
return ApiClient(
services: [
MyService.create(), // Register here
],
);
}
4. Repositories
Repositories bridge the gap between clients (API, Storage) and the Features.
- Rule: NEVER import from
src/features/intosrc/infrastructure/. - Pattern: Return
Result<T, Failure>for operations that can fail.
4.1 Define Error Codes & Failures
Errors are centralized. You must define a technical code and its translation.
- Register the Code in
lib/src/infrastructure/error_codes.dart:
enum AppErrorCode implements ErrorCode {
myFeature404('myFeature404'), // Key in i18n
// ...
}
- Add Translations under the
errorskey inlib/i18n/*.i18n.jsonfor ALL locales:
// en.i18n.json
"errors": {
"myFeature404": "The requested resource was not found."
}
// ar.i18n.json
"errors": {
"myFeature404": "لم يتم العثور على المورد المطلوب."
}
- Generate Translations: Run the following command to sync the JSON changes with the Dart code:
melos run translate
- Define the Failure:
final class MyApiFailure extends Failure {
const MyApiFailure({super.error}) : super(code: AppErrorCode.myFeature404);
}
4.2 Implementation
class MyRepository {
MyRepository({required MyService service}) : _service = service;
final MyService _service;
Future<Result<UserProfile, Failure>> getProfile() async {
try {
final response = await _service.getProfile();
if (response.isSuccessful) return Ok(response.body!);
return Err(MyApiFailure(error: response.error));
} catch (e) {
return Err(MyApiFailure(error: e));
}
}
}
5. Riverpod Integration
Provide the repository via a global provider. Obtain the required service
from the apiClientProvider.
@Riverpod(keepAlive: true)
MyRepository myRepository(Ref ref) {
final service = ref.watch(apiClientProvider).getService<MyService>();
return MyRepository(service: service);
}
6. Persistence
Use StorageClient for general persistence or SecureStorageClient for
sensitive data (tokens). Always use AppKeys for storage keys.
final storage = ref.watch(storageClientProvider);
await storage.write(AppKeys.settings, 'value');