Install
$ agentstack add skill-anoopsg-agent-rules-create-feature ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Feature Creation Skill
This skill outlines the standard process for creating a new feature in the project, following the DDD-lite architecture. This guide covers both simple domains (like Settings) and complex domains (like Auth).
1. Directory Structure
Features are located in lib/src/features/. The structure depends on the complexity of the domain.
A. Simple Domain (e.g., Settings, Explore)
If the feature is self-contained and doesn't require separate sub-modules, keep it flat under the feature root folder. No _self folder is used.
lib/src/features/settings/
├── state/ # Domain-specific state
│ └── settings_notifier.dart
├── view/ # UI implementation
│ ├── settings_page.dart # Entry point & Bindings
│ ├── settings_view.dart # Mobile UI
│ └── settings_view_web.dart # Web UI
└── settings.dart # Feature barrel file
B. Complex Domain (e.g., Auth, Account)
If the feature has multiple sub-domains, it is structured as a complex domain. The _self folder is used for root-level domain control, shared domain state (e.g., auth status, user metadata, cache), and layout/shell views that host or wrap the sub-features.
Example: Auth Domain
lib/src/features/auth/
├── _self/ # Shared domain logic & state
│ └── state/
│ └── auth_notifier.dart # Global auth status
├── login/ # Sub-domain feature
│ ├── state/ # Local feature state
│ │ └── login_notifier.dart
│ ├── view/ # Local UI
│ │ ├── login_page.dart
│ │ ├── login_view.dart
│ │ └── login_view_web.dart
│ └── login.dart # Sub-domain barrel
└── auth.dart # Domain barrel (exports _self and sub-domains)
2. Naming Conventions
| Entity | Convention | Example | | :--- | :--- | :--- | | Folder | snake_case | auth_login | | File | snake_case | login_page.dart | | Page Class | PascalCase + Page | LoginPage | | View Class | PascalCase + View | LoginView | | Web View Class | PascalCase + ViewWeb | LoginViewWeb | | Props Class | PascalCase + Props | LoginProps |
3. Creating the View Layer
3.1 The Page & Bindings
The Page widget delegates logic to _Bindings, which prepares the ViewProps.
class MyFeaturePage extends StatelessWidget {
const MyFeaturePage({super.key});
@override
Widget build(BuildContext context) {
return _Bindings(
viewBuilder: (_, props) {
// Simple pages can return the view directly.
// Complex UIs should use MemoizedView for performance.
if (isComplex) {
return MemoizedView(
props: props,
builder: (context, memProps) => kIsWeb
? MyFeatureViewWeb(props: memProps)
: MyFeatureView(props: memProps),
);
}
return kIsWeb
? MyFeatureViewWeb(props: props)
: MyFeatureView(props: props);
},
);
}
}
3.2 ViewProps & Stale Closures
ViewProps separates Data (used for equality) from Callbacks (ignored for equality).
> [!IMPORTANT] > CRITICAL RULE FOR AI: NEVER include callback functions (like VoidCallback, Function, etc.) in the equalityProperties list of a ViewProps subclass. The base class has a runtime assertion that will crash the app if functions are included. ONLY include data fields.
> [!CAUTION] > STALE CLOSURES: Because MemoizedView ignores callbacks in equality > checks, your UI might hold a reference to an "old" closure. > Never capture variables from ref.watch in a callback. Always use > ref.read inside the closure to ensure you have the fresh state.
final class MyProps extends ViewProps {
const MyProps({required this.data, required this.onAction});
final String data;
final VoidCallback onAction;
// CRITICAL: Only include data fields. Do NOT include functions/callbacks here!
@override
List get equalityProperties => [data]; // Exclude onAction
}
// ... inside _Bindings ...
return viewBuilder(
context,
MyProps(
data: ref.watch(myProvider).data, // Data for equality
onAction: () {
// SAFE: ref.read ensures fresh state when callback is executed
ref.read(myProvider.notifier).doSomething();
// DANGEROUS: capturing 'data' here will cause stale closure bugs
// print(data);
},
),
);
3.3 Feature State & Notifiers
Feature state and notifiers live in the state/ directory of a feature.
- State Location: Define the state class in the same file as the provider
or notifier. Do not create a separate file for the state class.
- Data Modeling: Always use
dart_mappable(@MappableClass()) for state
classes to get built-in immutability (copyWith) and equality.
import 'package:dart_mappable/dart_mappable.dart';
import 'package:framework/framework.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'login_notifier.mapper.dart';
part 'login_notifier.g.dart';
/// Local form state tracking for the Auth flow.
@MappableClass()
class LoginState with LoginStateMappable implements FailureInterface {
const LoginState({this.isLoggingIn = false, this.failure});
factory LoginState.loading() => const LoginState(isLoggingIn: true);
final bool isLoggingIn;
@override
final Failure? failure;
}
@riverpod
class LoginNotifier extends _$LoginNotifier {
@override
LoginState build() => const LoginState();
void login() {
state = LoginState.loading();
// Action logic here...
}
}
4. Routing
Follow the [Routing Management Skill](manage-routes.md) to define the route class and register it in the appropriate group (open, unauthenticated, or authenticated).
5. Barrel Files (Exports)
- Sub-domain level: Export the notifier and the page.
- Domain level: Export
_selfand all sub-domain barrels. - Global level (
features/features.dart): Export all domain barrels.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: anoopsg
- Source: anoopsg/agent_rules
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.