chore: update README and CLI usage for cursor_gen, version bump to 1.0.1

- Changed CLI usage instructions from `dart run cursor_gen` to `cursor_gen` for global activation.
- Updated project-brief.yaml example and README to reflect new command usage.
- Added app_context section in project-brief.yaml for theme variants and RBAC roles.
- Fixed bundled template resolution for local and global installs to prevent 'Template not found' errors.
- Version bump to 1.0.1 with corresponding updates in CHANGELOG and pubspec.yaml.
This commit is contained in:
2026-05-13 12:08:52 +05:30
parent b05cdb7fbe
commit 54c66efe9b
157 changed files with 8233 additions and 570 deletions
@@ -0,0 +1,49 @@
---
description: "Firebase conventions for {{PROJECT_NAME}}"
alwaysApply: true
---
# Firebase Standards — {{PROJECT_NAME}}
## Firestore
- Collection names: `camelCase` plural — `users`, `products`, `orderItems`
- Document IDs: use Firebase auto-IDs unless a natural key exists
- **Streams vs Futures**: use `snapshots()` for live data, `get()` for one-time reads
- Always handle `FirebaseException` explicitly — catch by `e.code` not generic `Exception`
- Paginate large collections with `startAfterDocument` — never fetch unbounded collections
```dart
// ✅ Stream-based real-time listener
Stream<List<Product>> watchProducts() {
return _firestore.collection('products')
.where('isActive', isEqualTo: true)
.snapshots()
.map((snap) => snap.docs.map(Product.fromDoc).toList());
}
// ✅ Handle FirebaseException by code
try {
await _firestore.collection('orders').add(order.toMap());
} on FirebaseException catch (e) {
switch (e.code) {
case 'permission-denied': throw AppError.authError('Insufficient permissions');
case 'unavailable': throw AppError.networkError();
default: throw AppError.unknown(e);
}
}
```
## Firebase Auth
- Always use `authStateChanges()` stream — never cache auth state locally
- Handle all error codes: `user-not-found`, `wrong-password`, `email-already-in-use`, `network-request-failed`
- Sign-out: clear all local state AND call `FirebaseAuth.instance.signOut()`
## Cloud Functions
- Call via `FirebaseFunctions.instance.httpsCallable('functionName')`
- Handle `FirebaseFunctionsException` with `.code` and `.message`
- Never expose internal errors to client — functions return structured error responses
## Security (complement to security-standards.mdc)
- Firestore Security Rules must be tested with the emulator before deploying
- No `allow read, write: if true` — even in development
- Rule coverage: every collection must have explicit rules
@@ -0,0 +1,25 @@
---
description: "Real-time feature conventions for {{PROJECT_NAME}}"
alwaysApply: true
---
# Real-time Features — {{PROJECT_NAME}}
## Connection management
- Always expose a `connectionState` stream — UI must show "offline" indicator
- Implement exponential backoff for reconnection (1s, 2s, 4s, 8s, max 60s)
- Cancel all subscriptions in `dispose()` — memory leaks are the #1 bug in real-time apps
## Offline-first strategy
- Cache last known state locally (Hive, Drift, or Isar)
- Show stale data with a "last updated" timestamp while reconnecting
- Queue mutations offline, replay on reconnect (use `connectivity_plus`)
## WebSocket / SSE
- Use `web_socket_channel` for WebSocket — never raw `dart:io` WebSocket
- Implement heartbeat/ping to detect dead connections
- Parse and validate all incoming messages — never trust raw server data
## UI indicators
- Show a persistent banner when offline: "You're offline — changes will sync when reconnected"
- Animate the banner away on reconnection — don't just hide it abruptly
@@ -0,0 +1,52 @@
---
description: "REST API conventions for {{PROJECT_NAME}}"
alwaysApply: true
---
# REST API Standards — {{PROJECT_NAME}}
## HTTP client setup (Dio)
```dart
// In core/network/dio_client.dart
Dio createDioClient({required AppConfig config}) {
final dio = Dio(BaseOptions(
baseUrl: config.baseUrl,
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 30),
headers: {'Content-Type': 'application/json', 'Accept': 'application/json'},
));
dio.interceptors.addAll([
AuthInterceptor(tokenStorage: getIt<TokenStorage>()),
LoggingInterceptor(), // debug builds only
RetryInterceptor(dio), // 3 retries on network errors
]);
return dio;
}
```
## DTOs (Data Transfer Objects)
- DTOs live in the `data/` layer — **never** pass raw JSON maps to the domain layer
- Use Freezed for DTOs if `codegen` includes `json_serializable` or `freezed`
- `fromJson` factory must handle null fields gracefully — API response fields are not guaranteed
## Error handling
```dart
// Map HTTP errors → AppError domain types
AppError _mapDioError(DioException e) => switch (e.type) {
DioExceptionType.connectionTimeout => const NetworkError(statusCode: null),
DioExceptionType.receiveTimeout => const NetworkError(statusCode: null),
DioExceptionType.badResponse => _mapStatusCode(e.response?.statusCode),
DioExceptionType.connectionError => const NetworkError(statusCode: null),
_ => UnknownError(e),
};
```
## API versioning
- Base URL includes version: `https://api.{{PROJECT_NAME}}.com/v1/`
- When upgrading API version, keep old version working until all clients migrate
## Auth token interceptor
- Inject `Authorization: Bearer <token>` automatically on every request
- On 401: refresh token once, retry original request, then logout if refresh fails
- On 403: map to `AppError.authError('Insufficient permissions')`
@@ -0,0 +1,52 @@
---
description: "Supabase conventions for {{PROJECT_NAME}}"
alwaysApply: true
---
# Supabase Standards — {{PROJECT_NAME}}
## Row Level Security (RLS) awareness
- **ALWAYS** assume RLS is enabled — never write queries that assume full table access
- Test queries in the Supabase dashboard before implementing in Flutter
- If a query returns empty unexpectedly, check RLS policies first
## Queries
```dart
// ✅ Type-safe query with error handling
Future<List<Product>> getProducts() async {
final response = await _supabase
.from('products')
.select('id, name, price, category_id, categories(name)')
.eq('is_active', true)
.order('created_at', ascending: false)
.limit(50);
return response.map(Product.fromMap).toList();
}
```
## Realtime subscriptions
```dart
StreamSubscription<List<Map<String, dynamic>>>? _sub;
void watchOrders(String userId) {
_sub = _supabase
.from('orders')
.stream(primaryKey: ['id'])
.eq('user_id', userId)
.listen(
(data) => _updateOrders(data),
onError: (e) => _handleError(e),
);
}
@override
void dispose() {
_sub?.cancel(); // ALWAYS cancel in dispose()
super.dispose();
}
```
## Auth session
- Use `supabase.auth.onAuthStateChange` stream — never poll auth state
- Persist session: `supabase-flutter` handles this automatically via secure storage
- `session.accessToken` expires — check `session.isExpired` before sensitive operations