From Bug Handler
Use when writing or refactoring feature code in an app that depends on bug_handler - repositories/services that can fail, cubits/blocs/notifiers/controllers that load data, error states, try/catch blocks, or "handle errors properly" requests. Also use when choosing which exception type to throw or how errors should reach the UI.
How this skill is triggered — by the user, by Claude, or both
Slash command
/bug-handler:guard-workflowThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The package's core contract, applied to everyday feature work. One rule chain:
The package's core contract, applied to everyday feature work. One rule chain:
repositories throw typed BaseException subclasses; state managers run work
through guard(...) with a source: label; state stores the BaseException
object; UI renders error.userMessage. Reporting happens inside guard and
never throws.
BugReportClient exists; if you see BugReporter/ErrorHandler,
switch to the migrate-legacy-api skill first).guard< and guard() - reuse it;BaseException subclasses (search extends ApiException,
extends BaseException) - throw those, do not invent parallels;BaseException subclass. Pick the type from
../../references/exceptions-catalog.md. Already-BaseException errors pass
through (on BaseException { rethrow; }); unknown ones become
UnexpectedException(cause: e, stack: s, devMessage: 'context...').parser(() => User.fromJson(json), data: json) - it throws a typed
ParsingException carrying the raw payload.guard(action, source: 'ClassName.method')
(or the app's wrapper over it). Never bare try/catch in controllers, and never
Riverpod's AsyncValue.guard where reporting is intended - it neither
normalizes nor reports nor labels the error.BaseException? error (or a sealed variant
holding it). Do NOT store e.userMessage strings - that discards severity,
type, and metadata the UI and retry logic need. If the app's shared state
container only holds a String today, keep the feature consistent with its
siblings but ALSO surface the typed object where the feature owns its state
class, and tell the user about the container limitation.error.userMessage; dev details (runtimeType, devMessage)
only behind a debug flag. userMessage must be a resolved human string -
never a localization KEY (a real production bug class: raw keys rendered to
users). Resolve l10n at construction or render time explicitly.source: convention: stable dotted ClassName.method (e.g.
'AuthController.login'). It feeds fingerprints, grouping, and dedupe.
Follow Pattern E in ../../references/production-patterns.md for full Riverpod
and Bloc examples (runner mixin, sealed state, switch (res) { case Ok... }).
Compressed shape for any library:
emitLoading();
final res = await guard(() => repo.fetch(), source: 'XController.load');
res.match(
ok: (data) => emitLoaded(data),
err: (e) => emitError(e), // the BaseException itself
);
guardSync for synchronous compute (reporting is fire-and-forget);
res.unwrapOr(fallback) / map / andThen for chaining;
ErrorBoundary.of(context).guardAsyncCallback when a widget-local fallback UI is
wanted (import package:bug_handler/flutter/error_boundary.dart).
When the backend has semantics the stock types miss, subclass per the
AppApiException template in ../../references/exceptions-catalog.md
(remember import 'package:bug_handler/helpers.dart' for HttpStatusCodeInfo).
Set severity deliberately - it is the delivery gate. isReportable: false
documents intent but is NOT enforced by the pipeline; do not rely on it to
suppress delivery.
dart analyze on touched files; dart format them.BaseException subclass and assert state.error is that instance - Equatable
makes this exact).catch ( - every catch in repositories must rethrow a
BaseException; every catch in controllers should not exist (guard replaced it).Err(e) into the app's failure type at
the edge, and preserve e (not just a message) inside it when possible."Add a favorites feature: repository + cubit + screen" -> repository methods
throw AppApiException.fromResponse(...) / use parser for models; cubit's
load()/toggle() run through guard(source: 'FavoritesCubit.load'); state is
sealed FavoritesState with FavoritesError(BaseException error); screen shows
error.userMessage with a retry that re-calls the cubit.
Guides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.
Dispatches multiple subagents concurrently for independent tasks without shared state. Use when facing 2+ unrelated failures or subsystems that can be investigated in parallel.
npx claudepluginhub omar-hanafy/bug_handler --plugin bug-handler