oh-my-customcode 0.30.7 → 0.30.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -13
- package/package.json +1 -1
- package/templates/.claude/agents/fe-flutter-agent.md +46 -0
- package/templates/.claude/ontology/agents.yaml +14 -2
- package/templates/.claude/ontology/graphs/agent-skill.json +2 -0
- package/templates/.claude/ontology/graphs/full-graph.json +13 -1
- package/templates/.claude/ontology/graphs/routing.json +2 -0
- package/templates/.claude/ontology/graphs/skill-rule.json +1 -0
- package/templates/.claude/ontology/skills.yaml +69 -59
- package/templates/.claude/skills/analysis/SKILL.md +1 -0
- package/templates/.claude/skills/dev-lead-routing/SKILL.md +4 -2
- package/templates/.claude/skills/flutter-best-practices/SKILL.md +431 -0
- package/templates/.claude/skills/intent-detection/patterns/agent-triggers.yaml +8 -0
- package/templates/guides/flutter/architecture.md +141 -0
- package/templates/guides/flutter/fundamentals.md +119 -0
- package/templates/guides/flutter/index.yaml +44 -0
- package/templates/guides/flutter/performance.md +119 -0
- package/templates/guides/flutter/state-management.md +144 -0
- package/templates/guides/flutter/testing.md +155 -0
- package/templates/guides/index.yaml +8 -0
- package/templates/manifest.json +4 -4
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Flutter Guide
|
|
2
|
+
|
|
3
|
+
metadata:
|
|
4
|
+
name: flutter
|
|
5
|
+
description: Flutter cross-platform development reference documentation
|
|
6
|
+
|
|
7
|
+
source:
|
|
8
|
+
type: external
|
|
9
|
+
origin: flutter.dev
|
|
10
|
+
urls:
|
|
11
|
+
- https://docs.flutter.dev/
|
|
12
|
+
- https://docs.flutter.dev/app-architecture
|
|
13
|
+
- https://riverpod.dev/docs/introduction/getting_started
|
|
14
|
+
- https://bloclibrary.dev/
|
|
15
|
+
- https://dart.dev/effective-dart
|
|
16
|
+
last_fetched: "2026-03-11"
|
|
17
|
+
|
|
18
|
+
documents:
|
|
19
|
+
- name: fundamentals
|
|
20
|
+
path: ./fundamentals.md
|
|
21
|
+
description: Flutter widget system, BuildContext, rendering pipeline
|
|
22
|
+
|
|
23
|
+
- name: state-management
|
|
24
|
+
path: ./state-management.md
|
|
25
|
+
description: Riverpod 3.0, BLoC 9.0, Provider patterns and selection guide
|
|
26
|
+
|
|
27
|
+
- name: architecture
|
|
28
|
+
path: ./architecture.md
|
|
29
|
+
description: MVVM structure, project organization, go_router, freezed
|
|
30
|
+
|
|
31
|
+
- name: performance
|
|
32
|
+
path: ./performance.md
|
|
33
|
+
description: DevTools, const optimization, RepaintBoundary, Isolate
|
|
34
|
+
|
|
35
|
+
- name: testing
|
|
36
|
+
path: ./testing.md
|
|
37
|
+
description: Widget tests, golden tests, integration tests, mocking
|
|
38
|
+
|
|
39
|
+
- name: security
|
|
40
|
+
path: ./security.md
|
|
41
|
+
description: OWASP Mobile Top 10, secure storage, certificate pinning, platform security
|
|
42
|
+
|
|
43
|
+
used_by:
|
|
44
|
+
- fe-flutter-agent
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Flutter Performance
|
|
2
|
+
|
|
3
|
+
> Reference: docs.flutter.dev/perf/best-practices
|
|
4
|
+
|
|
5
|
+
## Frame Budget
|
|
6
|
+
|
|
7
|
+
- Target: **<8ms build + <8ms render = 16.67ms** (60fps)
|
|
8
|
+
- Profile with: `flutter run --profile` (not debug mode)
|
|
9
|
+
- Tool: DevTools Performance view → identify jank frames
|
|
10
|
+
|
|
11
|
+
## Build-Time Optimization
|
|
12
|
+
|
|
13
|
+
### const Constructors
|
|
14
|
+
|
|
15
|
+
```dart
|
|
16
|
+
// GOOD — zero rebuild cost
|
|
17
|
+
const Text('Hello');
|
|
18
|
+
const SizedBox(height: 8);
|
|
19
|
+
const Icon(Icons.star);
|
|
20
|
+
|
|
21
|
+
// BAD — new instance every build
|
|
22
|
+
Text('Hello');
|
|
23
|
+
SizedBox(height: 8);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Localize setState
|
|
27
|
+
|
|
28
|
+
```dart
|
|
29
|
+
// BAD — rebuilds entire screen
|
|
30
|
+
class _ScreenState extends State<Screen> {
|
|
31
|
+
int count = 0;
|
|
32
|
+
Widget build(BuildContext context) {
|
|
33
|
+
return Column(children: [
|
|
34
|
+
ExpensiveHeader(), // rebuilt unnecessarily
|
|
35
|
+
Text('$count'),
|
|
36
|
+
ElevatedButton(onPressed: () => setState(() => count++), child: Text('+'))
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// GOOD — only counter rebuilds
|
|
42
|
+
class _ScreenState extends State<Screen> {
|
|
43
|
+
Widget build(BuildContext context) {
|
|
44
|
+
return Column(children: [
|
|
45
|
+
const ExpensiveHeader(), // not rebuilt
|
|
46
|
+
CounterWidget(), // isolated StatefulWidget
|
|
47
|
+
]);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Extract Widgets (not methods)
|
|
53
|
+
|
|
54
|
+
```dart
|
|
55
|
+
// BAD — no Element identity, always rebuilds
|
|
56
|
+
Widget _buildHeader() => Container(...);
|
|
57
|
+
|
|
58
|
+
// GOOD — has Element identity, diffed efficiently
|
|
59
|
+
class HeaderWidget extends StatelessWidget {
|
|
60
|
+
const HeaderWidget({super.key});
|
|
61
|
+
@override
|
|
62
|
+
Widget build(BuildContext context) => Container(...);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Rendering Optimization
|
|
67
|
+
|
|
68
|
+
### RepaintBoundary
|
|
69
|
+
|
|
70
|
+
```dart
|
|
71
|
+
// Wrap frequently repainting subtrees
|
|
72
|
+
RepaintBoundary(
|
|
73
|
+
child: AnimatedWidget(...), // only this subtree repaints
|
|
74
|
+
)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Detect with: DevTools → Rendering → "Highlight repaints"
|
|
78
|
+
|
|
79
|
+
### Avoid Expensive Widgets
|
|
80
|
+
|
|
81
|
+
| Avoid | Use Instead | Reason |
|
|
82
|
+
|-------|-------------|--------|
|
|
83
|
+
| `Opacity` widget | `color.withValues(alpha: 0.5)` | Opacity widget triggers saveLayer |
|
|
84
|
+
| `ClipRRect` in animations | Pre-clip static content | saveLayer per frame |
|
|
85
|
+
| `Container` for sizing | `SizedBox` | Lighter, no decoration |
|
|
86
|
+
|
|
87
|
+
## List Performance
|
|
88
|
+
|
|
89
|
+
```dart
|
|
90
|
+
// GOOD — lazy construction, O(visible) not O(total)
|
|
91
|
+
ListView.builder(
|
|
92
|
+
itemCount: items.length,
|
|
93
|
+
itemExtent: 72.0, // skip intrinsic layout passes
|
|
94
|
+
itemBuilder: (context, index) => ProductTile(items[index]),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// BAD — builds ALL children upfront
|
|
98
|
+
ListView(children: items.map((i) => ProductTile(i)).toList());
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Compute Offloading
|
|
102
|
+
|
|
103
|
+
```dart
|
|
104
|
+
// CPU-intensive work (>16ms) on isolate
|
|
105
|
+
final result = await Isolate.run(() {
|
|
106
|
+
return heavyJsonParsing(rawData);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Web-compatible alternative
|
|
110
|
+
final result = await compute(heavyJsonParsing, rawData);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## DevTools Workflow
|
|
114
|
+
|
|
115
|
+
1. **Inspector** → identify widget causing rebuild
|
|
116
|
+
2. **Performance view** → identify jank frames (>16ms)
|
|
117
|
+
3. **CPU Profiler** → identify expensive Dart methods
|
|
118
|
+
4. **Memory view** → detect object leaks
|
|
119
|
+
5. **Network** → monitor API call timing
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Flutter State Management
|
|
2
|
+
|
|
3
|
+
> Reference: riverpod.dev, bloclibrary.dev, docs.flutter.dev/data-and-backend/state-mgmt
|
|
4
|
+
|
|
5
|
+
## Selection Guide
|
|
6
|
+
|
|
7
|
+
| Approach | Complexity | Scalability | Best For |
|
|
8
|
+
|----------|-----------|-------------|----------|
|
|
9
|
+
| **Riverpod 3.0** | Medium | Excellent | New projects (default) |
|
|
10
|
+
| **BLoC 9.0** | High | Excellent | Enterprise, regulated industries |
|
|
11
|
+
| **Provider** | Low | Moderate | Simple apps, learning |
|
|
12
|
+
| **setState** | Low | Poor | Ephemeral local UI state |
|
|
13
|
+
| **GetX** | Low | Poor | **AVOID** for new projects |
|
|
14
|
+
|
|
15
|
+
## Riverpod 3.0 (Default)
|
|
16
|
+
|
|
17
|
+
### Provider Types
|
|
18
|
+
|
|
19
|
+
```dart
|
|
20
|
+
// Simple value provider
|
|
21
|
+
@riverpod
|
|
22
|
+
String greeting(Ref ref) => 'Hello, World!';
|
|
23
|
+
|
|
24
|
+
// Async data provider
|
|
25
|
+
@riverpod
|
|
26
|
+
Future<List<Product>> products(Ref ref) async {
|
|
27
|
+
final api = ref.watch(apiClientProvider);
|
|
28
|
+
return api.getProducts();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Stateful notifier
|
|
32
|
+
@riverpod
|
|
33
|
+
class Counter extends _$Counter {
|
|
34
|
+
@override
|
|
35
|
+
int build() => 0;
|
|
36
|
+
|
|
37
|
+
void increment() => state++;
|
|
38
|
+
void decrement() => state--;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Async stateful notifier
|
|
42
|
+
@riverpod
|
|
43
|
+
class ProductList extends _$ProductList {
|
|
44
|
+
@override
|
|
45
|
+
Future<List<Product>> build() async {
|
|
46
|
+
return ref.watch(productRepositoryProvider).getAll();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Future<void> addProduct(Product product) async {
|
|
50
|
+
await ref.read(productRepositoryProvider).add(product);
|
|
51
|
+
ref.invalidateSelf();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### UI Consumption
|
|
57
|
+
|
|
58
|
+
```dart
|
|
59
|
+
class ProductListScreen extends ConsumerWidget {
|
|
60
|
+
@override
|
|
61
|
+
Widget build(BuildContext context, WidgetRef ref) {
|
|
62
|
+
final state = ref.watch(productListProvider);
|
|
63
|
+
|
|
64
|
+
return state.when(
|
|
65
|
+
loading: () => const CircularProgressIndicator(),
|
|
66
|
+
error: (e, st) => Text('Error: $e'),
|
|
67
|
+
data: (products) => ListView.builder(
|
|
68
|
+
itemCount: products.length,
|
|
69
|
+
itemBuilder: (_, i) => ProductCard(products[i]),
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Key Rules
|
|
77
|
+
|
|
78
|
+
- `ref.watch()` in build methods only (reactive)
|
|
79
|
+
- `ref.read()` in callbacks and event handlers (one-time)
|
|
80
|
+
- Never call `ref.watch()` inside non-build methods
|
|
81
|
+
- Use `family` for parameterized providers
|
|
82
|
+
- Use `keepAlive` sparingly (expensive computations only)
|
|
83
|
+
|
|
84
|
+
## BLoC 9.0 (Enterprise)
|
|
85
|
+
|
|
86
|
+
### Cubit (Simple)
|
|
87
|
+
|
|
88
|
+
```dart
|
|
89
|
+
class CounterCubit extends Cubit<int> {
|
|
90
|
+
CounterCubit() : super(0);
|
|
91
|
+
|
|
92
|
+
void increment() => emit(state + 1);
|
|
93
|
+
void decrement() => emit(state - 1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// UI
|
|
97
|
+
BlocBuilder<CounterCubit, int>(
|
|
98
|
+
builder: (context, count) => Text('$count'),
|
|
99
|
+
)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Bloc (Full Events)
|
|
103
|
+
|
|
104
|
+
```dart
|
|
105
|
+
// Events
|
|
106
|
+
sealed class AuthEvent {}
|
|
107
|
+
class LoginRequested extends AuthEvent {
|
|
108
|
+
final String email, password;
|
|
109
|
+
LoginRequested(this.email, this.password);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// States
|
|
113
|
+
sealed class AuthState {}
|
|
114
|
+
class AuthInitial extends AuthState {}
|
|
115
|
+
class AuthLoading extends AuthState {}
|
|
116
|
+
class AuthSuccess extends AuthState { final User user; AuthSuccess(this.user); }
|
|
117
|
+
class AuthFailure extends AuthState { final String error; AuthFailure(this.error); }
|
|
118
|
+
|
|
119
|
+
// Bloc
|
|
120
|
+
class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|
121
|
+
AuthBloc(this._authRepo) : super(AuthInitial()) {
|
|
122
|
+
on<LoginRequested>(_onLogin);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
final AuthRepository _authRepo;
|
|
126
|
+
|
|
127
|
+
Future<void> _onLogin(LoginRequested event, Emitter<AuthState> emit) async {
|
|
128
|
+
emit(AuthLoading());
|
|
129
|
+
final result = await _authRepo.login(event.email, event.password);
|
|
130
|
+
switch (result) {
|
|
131
|
+
case Ok(:final value): emit(AuthSuccess(value));
|
|
132
|
+
case Error(:final error): emit(AuthFailure(error.toString()));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Key Rules
|
|
139
|
+
|
|
140
|
+
- One event per user action
|
|
141
|
+
- Cubit for simple state, Bloc when audit trail needed
|
|
142
|
+
- Never emit state in constructor
|
|
143
|
+
- `BlocListener` for side effects, `BlocBuilder` for UI
|
|
144
|
+
- Cancel subscriptions in `close()`
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Flutter Testing
|
|
2
|
+
|
|
3
|
+
> Reference: docs.flutter.dev/testing/overview
|
|
4
|
+
|
|
5
|
+
## Test Pyramid
|
|
6
|
+
|
|
7
|
+
| Level | Speed | Confidence | Tool |
|
|
8
|
+
|-------|-------|------------|------|
|
|
9
|
+
| Unit | Fast | Low | `flutter_test` |
|
|
10
|
+
| Widget | Fast | Medium | `testWidgets`, `WidgetTester` |
|
|
11
|
+
| Integration | Slow | High | `integration_test` package |
|
|
12
|
+
| Golden | Fast | Visual | `matchesGoldenFile()` |
|
|
13
|
+
|
|
14
|
+
## Widget Tests (Primary)
|
|
15
|
+
|
|
16
|
+
```dart
|
|
17
|
+
testWidgets('ProductCard displays name and price', (tester) async {
|
|
18
|
+
await tester.pumpWidget(
|
|
19
|
+
MaterialApp(
|
|
20
|
+
home: ProductCard(
|
|
21
|
+
product: Product(id: 1, name: 'Widget', price: 9.99),
|
|
22
|
+
),
|
|
23
|
+
),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
expect(find.text('Widget'), findsOneWidget);
|
|
27
|
+
expect(find.text('\$9.99'), findsOneWidget);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
testWidgets('tapping add button calls onAdd', (tester) async {
|
|
31
|
+
var called = false;
|
|
32
|
+
await tester.pumpWidget(
|
|
33
|
+
MaterialApp(
|
|
34
|
+
home: AddButton(onAdd: () => called = true),
|
|
35
|
+
),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
await tester.tap(find.byType(ElevatedButton));
|
|
39
|
+
expect(called, isTrue);
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Riverpod Testing
|
|
44
|
+
|
|
45
|
+
```dart
|
|
46
|
+
testWidgets('ProductListScreen shows products', (tester) async {
|
|
47
|
+
await tester.pumpWidget(
|
|
48
|
+
ProviderScope(
|
|
49
|
+
overrides: [
|
|
50
|
+
productListProvider.overrideWith(
|
|
51
|
+
() => FakeProductListNotifier(),
|
|
52
|
+
),
|
|
53
|
+
],
|
|
54
|
+
child: const MaterialApp(home: ProductListScreen()),
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
await tester.pumpAndSettle();
|
|
59
|
+
expect(find.byType(ProductCard), findsNWidgets(3));
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## BLoC Testing
|
|
64
|
+
|
|
65
|
+
```dart
|
|
66
|
+
blocTest<CounterCubit, int>(
|
|
67
|
+
'increment emits [1] when initial state is 0',
|
|
68
|
+
build: () => CounterCubit(),
|
|
69
|
+
act: (cubit) => cubit.increment(),
|
|
70
|
+
expect: () => [1],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
blocTest<AuthBloc, AuthState>(
|
|
74
|
+
'login emits [loading, success] on valid credentials',
|
|
75
|
+
build: () => AuthBloc(FakeAuthRepository()),
|
|
76
|
+
act: (bloc) => bloc.add(LoginRequested('user@test.com', 'pass123')),
|
|
77
|
+
expect: () => [
|
|
78
|
+
isA<AuthLoading>(),
|
|
79
|
+
isA<AuthSuccess>(),
|
|
80
|
+
],
|
|
81
|
+
);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Mocking with mocktail
|
|
85
|
+
|
|
86
|
+
```dart
|
|
87
|
+
class MockProductRepository extends Mock implements ProductRepository {}
|
|
88
|
+
|
|
89
|
+
void main() {
|
|
90
|
+
late MockProductRepository mockRepo;
|
|
91
|
+
|
|
92
|
+
setUp(() {
|
|
93
|
+
mockRepo = MockProductRepository();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('getProducts returns list', () async {
|
|
97
|
+
when(() => mockRepo.getAll()).thenAnswer(
|
|
98
|
+
(_) async => Ok([Product(id: 1, name: 'Test', price: 9.99)]),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
final result = await mockRepo.getAll();
|
|
102
|
+
expect(result, isA<Ok<List<Product>>>());
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Golden Tests
|
|
108
|
+
|
|
109
|
+
```dart
|
|
110
|
+
testWidgets('ProductCard matches golden', (tester) async {
|
|
111
|
+
await tester.pumpWidget(
|
|
112
|
+
MaterialApp(
|
|
113
|
+
home: ProductCard(product: testProduct),
|
|
114
|
+
),
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
await expectLater(
|
|
118
|
+
find.byType(ProductCard),
|
|
119
|
+
matchesGoldenFile('goldens/product_card.png'),
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Update goldens: flutter test --update-goldens
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Accessibility Testing
|
|
127
|
+
|
|
128
|
+
```dart
|
|
129
|
+
testWidgets('meets accessibility guidelines', (tester) async {
|
|
130
|
+
final handle = tester.ensureSemantics();
|
|
131
|
+
await tester.pumpWidget(const MaterialApp(home: MyScreen()));
|
|
132
|
+
|
|
133
|
+
await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
|
|
134
|
+
await expectLater(tester, meetsGuideline(iOSTapTargetGuideline));
|
|
135
|
+
await expectLater(tester, meetsGuideline(textContrastGuideline));
|
|
136
|
+
|
|
137
|
+
handle.dispose();
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Test Organization
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
test/
|
|
145
|
+
├── unit/
|
|
146
|
+
│ ├── repositories/product_repository_test.dart
|
|
147
|
+
│ └── viewmodels/home_viewmodel_test.dart
|
|
148
|
+
├── widget/
|
|
149
|
+
│ ├── screens/product_list_screen_test.dart
|
|
150
|
+
│ └── widgets/product_card_test.dart
|
|
151
|
+
├── goldens/
|
|
152
|
+
│ └── product_card.png
|
|
153
|
+
integration_test/
|
|
154
|
+
└── app_test.dart
|
|
155
|
+
```
|
|
@@ -9,6 +9,14 @@ guides:
|
|
|
9
9
|
type: internal
|
|
10
10
|
|
|
11
11
|
# Frontend
|
|
12
|
+
- name: flutter
|
|
13
|
+
description: Flutter/Dart development patterns and best practices
|
|
14
|
+
path: ./flutter/
|
|
15
|
+
source:
|
|
16
|
+
type: external
|
|
17
|
+
origin: flutter.dev
|
|
18
|
+
url: https://docs.flutter.dev/
|
|
19
|
+
|
|
12
20
|
- name: web-design
|
|
13
21
|
description: Web design best practices and accessibility reference
|
|
14
22
|
path: ./web-design/
|
package/templates/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.30.
|
|
2
|
+
"version": "0.30.9",
|
|
3
3
|
"lastUpdated": "2026-03-09T00:00:00.000Z",
|
|
4
4
|
"components": [
|
|
5
5
|
{
|
|
@@ -12,19 +12,19 @@
|
|
|
12
12
|
"name": "agents",
|
|
13
13
|
"path": ".claude/agents",
|
|
14
14
|
"description": "AI agent definitions (flat .md files with prefixes)",
|
|
15
|
-
"files":
|
|
15
|
+
"files": 44
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"name": "skills",
|
|
19
19
|
"path": ".claude/skills",
|
|
20
20
|
"description": "Reusable skill modules (includes slash commands)",
|
|
21
|
-
"files":
|
|
21
|
+
"files": 68
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
"name": "guides",
|
|
25
25
|
"path": "guides",
|
|
26
26
|
"description": "Reference documentation",
|
|
27
|
-
"files":
|
|
27
|
+
"files": 24
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
"name": "hooks",
|