codecruise 0.1.0

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.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/bin/codecruise.js +68 -0
  4. package/config/CLAUDE.md +107 -0
  5. package/config/agents/analyst.md +48 -0
  6. package/config/agents/architect-reviewer.md +161 -0
  7. package/config/agents/architect.md +119 -0
  8. package/config/agents/critic.md +63 -0
  9. package/config/agents/developer.md +96 -0
  10. package/config/agents/devops.md +81 -0
  11. package/config/agents/orchestrator.md +91 -0
  12. package/config/agents/planner.md +139 -0
  13. package/config/agents/retro.md +52 -0
  14. package/config/agents/reviewer.md +101 -0
  15. package/config/agents/security-reviewer.md +57 -0
  16. package/config/agents/stack/expo/AGENT.md +473 -0
  17. package/config/agents/stack/expo/rules/critical.md +427 -0
  18. package/config/agents/stack/expo/rules/native.md +455 -0
  19. package/config/agents/stack/expo/rules/navigation.md +445 -0
  20. package/config/agents/stack/expo/rules/performance.md +415 -0
  21. package/config/agents/stack/fastify/AGENT.md +397 -0
  22. package/config/agents/stack/fastify/rules/api-design.md +283 -0
  23. package/config/agents/stack/fastify/rules/critical.md +232 -0
  24. package/config/agents/stack/fastify/rules/queues.md +303 -0
  25. package/config/agents/stack/fastify/rules/security.md +384 -0
  26. package/config/agents/stack/index.yaml +48 -0
  27. package/config/agents/stack/nextjs/AGENT.md +421 -0
  28. package/config/agents/stack/nextjs/rules/components.md +413 -0
  29. package/config/agents/stack/nextjs/rules/critical.md +391 -0
  30. package/config/agents/stack/nextjs/rules/performance.md +403 -0
  31. package/config/agents/stack/nextjs/rules/styling.md +334 -0
  32. package/config/agents/stack/shared-ts/AGENT.md +384 -0
  33. package/config/agents/stack/shared-ts/rules/critical.md +315 -0
  34. package/config/agents/stack/shared-ts/rules/patterns.md +384 -0
  35. package/config/agents/stack/shared-ts/rules/zod.md +427 -0
  36. package/config/agents/tester.md +79 -0
  37. package/config/commands/architect-discuss.md +366 -0
  38. package/config/commands/architect-list.md +160 -0
  39. package/config/commands/architect-review.md +111 -0
  40. package/config/commands/architect.md +118 -0
  41. package/config/commands/compact.md +118 -0
  42. package/config/commands/companion.md +279 -0
  43. package/config/commands/dashboard.md +152 -0
  44. package/config/commands/doctor.md +227 -0
  45. package/config/commands/dogfood-report.md +101 -0
  46. package/config/commands/flags/run-autonomous.md +110 -0
  47. package/config/commands/flags/run-pause.md +80 -0
  48. package/config/commands/ingest.md +173 -0
  49. package/config/commands/init.md +128 -0
  50. package/config/commands/metrics.md +87 -0
  51. package/config/commands/parallel.md +320 -0
  52. package/config/commands/pause.md +55 -0
  53. package/config/commands/plan-review.md +130 -0
  54. package/config/commands/plan.md +216 -0
  55. package/config/commands/production-check.md +308 -0
  56. package/config/commands/refine.md +323 -0
  57. package/config/commands/resume.md +72 -0
  58. package/config/commands/retro.md +121 -0
  59. package/config/commands/retry.md +75 -0
  60. package/config/commands/role.md +310 -0
  61. package/config/commands/run.md +417 -0
  62. package/config/commands/scope.md +85 -0
  63. package/config/commands/setup-permissions.md +104 -0
  64. package/config/commands/skip.md +75 -0
  65. package/config/commands/spec-forge.md +213 -0
  66. package/config/commands/spec-help.md +194 -0
  67. package/config/commands/spec-patch.md +342 -0
  68. package/config/commands/spec-resolve.md +110 -0
  69. package/config/commands/spec-review.md +153 -0
  70. package/config/commands/status.md +114 -0
  71. package/config/commands/sync.md +131 -0
  72. package/config/commands/task.md +138 -0
  73. package/config/commands/verify.md +124 -0
  74. package/config/hooks/README.md +632 -0
  75. package/config/hooks/activity-log.sh +187 -0
  76. package/config/hooks/anti-rationalize.sh +52 -0
  77. package/config/hooks/capture-verification.sh +112 -0
  78. package/config/hooks/collect-metrics.sh +135 -0
  79. package/config/hooks/enforce-file-scope.sh +75 -0
  80. package/config/hooks/enforce-state-machine.sh +161 -0
  81. package/config/hooks/enforce-tdd.sh +180 -0
  82. package/config/hooks/format.sh +40 -0
  83. package/config/hooks/lib/activity-helpers.sh +162 -0
  84. package/config/hooks/lib/read-settings.sh +71 -0
  85. package/config/hooks/load-context-skills.sh +95 -0
  86. package/config/hooks/notify.sh +81 -0
  87. package/config/hooks/pre-commit.sample +35 -0
  88. package/config/hooks/protect-files.sh +63 -0
  89. package/config/hooks/track-agents.sh +41 -0
  90. package/config/hooks/track-commands.sh +37 -0
  91. package/config/hooks/track-enforcement.sh +44 -0
  92. package/config/hooks/track-ooda.sh +77 -0
  93. package/config/hooks/validate-commit-msg.sh +35 -0
  94. package/config/hooks/validate-plan.sh +213 -0
  95. package/config/hooks/verify-criteria.sh +46 -0
  96. package/config/hooks/verify-todo-completion.sh +140 -0
  97. package/config/rules/comments.md +25 -0
  98. package/config/rules/decision-rules.md +308 -0
  99. package/config/rules/hygiene.md +247 -0
  100. package/config/rules/pattern-detection.md +372 -0
  101. package/config/rules/profiles.md +193 -0
  102. package/config/rules/recovery.md +83 -0
  103. package/config/rules/scope-detection.md +213 -0
  104. package/config/rules/standards.md +127 -0
  105. package/config/rules/workflow.md +121 -0
  106. package/config/schemas.md +767 -0
  107. package/config/settings.json +195 -0
  108. package/config/skills/backend/SKILL.md +734 -0
  109. package/config/skills/database/SKILL.md +426 -0
  110. package/config/skills/frontend/SKILL.md +434 -0
  111. package/config/skills/git/SKILL.md +396 -0
  112. package/config/skills/index.yaml +36 -0
  113. package/config/skills/observability/SKILL.md +430 -0
  114. package/config/skills/package-dev/SKILL.md +498 -0
  115. package/config/skills/performance/SKILL.md +378 -0
  116. package/config/skills/resilience/SKILL.md +573 -0
  117. package/config/skills/testing/SKILL.md +398 -0
  118. package/config/skills/testing-patterns/SKILL.md +276 -0
  119. package/config/skills/typescript/SKILL.md +152 -0
  120. package/config/templates/CLAUDE.md +70 -0
  121. package/config/templates/README.md +117 -0
  122. package/config/templates/steering/adr-template.md +102 -0
  123. package/config/templates/steering/product.md +60 -0
  124. package/config/templates/steering/rfc-template.md +159 -0
  125. package/config/templates/steering/structure.md +146 -0
  126. package/config/templates/steering/tech.md +85 -0
  127. package/package.json +40 -0
  128. package/src/install.js +163 -0
  129. package/src/report.js +310 -0
@@ -0,0 +1,455 @@
1
+ # Native Features - Expo SDK
2
+
3
+ Patterns for native APIs and device features.
4
+
5
+ ---
6
+
7
+ ## Storage
8
+
9
+ ### NATIVE-001: Secure storage for sensitive data
10
+
11
+ ```typescript
12
+ import * as SecureStore from 'expo-secure-store';
13
+
14
+ const secureStorage = {
15
+ async get(key: string): Promise<string | null> {
16
+ try {
17
+ return await SecureStore.getItemAsync(key);
18
+ } catch {
19
+ return null;
20
+ }
21
+ },
22
+
23
+ async set(key: string, value: string): Promise<void> {
24
+ await SecureStore.setItemAsync(key, value);
25
+ },
26
+
27
+ async remove(key: string): Promise<void> {
28
+ await SecureStore.deleteItemAsync(key);
29
+ },
30
+ };
31
+
32
+ // Use for: tokens, credentials, sensitive user data
33
+ await secureStorage.set('auth_token', token);
34
+ ```
35
+
36
+ ### NATIVE-002: AsyncStorage for preferences
37
+
38
+ ```typescript
39
+ import AsyncStorage from '@react-native-async-storage/async-storage';
40
+
41
+ const storage = {
42
+ async get<T>(key: string): Promise<T | null> {
43
+ const value = await AsyncStorage.getItem(key);
44
+ return value ? JSON.parse(value) : null;
45
+ },
46
+
47
+ async set<T>(key: string, value: T): Promise<void> {
48
+ await AsyncStorage.setItem(key, JSON.stringify(value));
49
+ },
50
+
51
+ async remove(key: string): Promise<void> {
52
+ await AsyncStorage.removeItem(key);
53
+ },
54
+ };
55
+
56
+ // Use for: preferences, cache, non-sensitive data
57
+ await storage.set('theme', 'dark');
58
+ await storage.set('onboarded', true);
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Camera & Images
64
+
65
+ ### NATIVE-003: Image picker
66
+
67
+ ```typescript
68
+ import * as ImagePicker from 'expo-image-picker';
69
+
70
+ async function pickImage() {
71
+ // Request permission
72
+ const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
73
+
74
+ if (status !== 'granted') {
75
+ Alert.alert('Permission needed', 'Please allow access to photos.');
76
+ return null;
77
+ }
78
+
79
+ const result = await ImagePicker.launchImageLibraryAsync({
80
+ mediaTypes: ImagePicker.MediaTypeOptions.Images,
81
+ allowsEditing: true,
82
+ aspect: [1, 1],
83
+ quality: 0.8,
84
+ });
85
+
86
+ if (result.canceled) return null;
87
+
88
+ return result.assets[0];
89
+ }
90
+
91
+ async function takePhoto() {
92
+ const { status } = await ImagePicker.requestCameraPermissionsAsync();
93
+
94
+ if (status !== 'granted') {
95
+ Alert.alert('Permission needed', 'Please allow camera access.');
96
+ return null;
97
+ }
98
+
99
+ const result = await ImagePicker.launchCameraAsync({
100
+ allowsEditing: true,
101
+ aspect: [1, 1],
102
+ quality: 0.8,
103
+ });
104
+
105
+ if (result.canceled) return null;
106
+
107
+ return result.assets[0];
108
+ }
109
+ ```
110
+
111
+ ### NATIVE-004: Image upload
112
+
113
+ ```typescript
114
+ async function uploadImage(uri: string) {
115
+ const formData = new FormData();
116
+
117
+ formData.append('image', {
118
+ uri,
119
+ type: 'image/jpeg',
120
+ name: 'photo.jpg',
121
+ } as any);
122
+
123
+ const response = await fetch('/api/upload', {
124
+ method: 'POST',
125
+ body: formData,
126
+ headers: {
127
+ 'Content-Type': 'multipart/form-data',
128
+ },
129
+ });
130
+
131
+ return response.json();
132
+ }
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Location
138
+
139
+ ### NATIVE-005: Get current location
140
+
141
+ ```typescript
142
+ import * as Location from 'expo-location';
143
+
144
+ async function getCurrentLocation() {
145
+ const { status } = await Location.requestForegroundPermissionsAsync();
146
+
147
+ if (status !== 'granted') {
148
+ throw new Error('Location permission denied');
149
+ }
150
+
151
+ const location = await Location.getCurrentPositionAsync({
152
+ accuracy: Location.Accuracy.Balanced,
153
+ });
154
+
155
+ return {
156
+ latitude: location.coords.latitude,
157
+ longitude: location.coords.longitude,
158
+ };
159
+ }
160
+ ```
161
+
162
+ ### NATIVE-006: Watch location
163
+
164
+ ```typescript
165
+ import * as Location from 'expo-location';
166
+
167
+ function useLocation() {
168
+ const [location, setLocation] = useState<Location.LocationObject | null>(null);
169
+
170
+ useEffect(() => {
171
+ let subscription: Location.LocationSubscription;
172
+
173
+ (async () => {
174
+ const { status } = await Location.requestForegroundPermissionsAsync();
175
+ if (status !== 'granted') return;
176
+
177
+ subscription = await Location.watchPositionAsync(
178
+ {
179
+ accuracy: Location.Accuracy.High,
180
+ timeInterval: 5000,
181
+ distanceInterval: 10,
182
+ },
183
+ (newLocation) => {
184
+ setLocation(newLocation);
185
+ }
186
+ );
187
+ })();
188
+
189
+ return () => subscription?.remove();
190
+ }, []);
191
+
192
+ return location;
193
+ }
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Push Notifications
199
+
200
+ ### NATIVE-007: Register for notifications
201
+
202
+ ```typescript
203
+ import * as Notifications from 'expo-notifications';
204
+ import * as Device from 'expo-device';
205
+ import { Platform } from 'react-native';
206
+
207
+ // Configure notification handling
208
+ Notifications.setNotificationHandler({
209
+ handleNotification: async () => ({
210
+ shouldShowAlert: true,
211
+ shouldPlaySound: true,
212
+ shouldSetBadge: true,
213
+ }),
214
+ });
215
+
216
+ async function registerForPushNotifications() {
217
+ if (!Device.isDevice) {
218
+ console.log('Push notifications require a physical device');
219
+ return null;
220
+ }
221
+
222
+ const { status: existingStatus } = await Notifications.getPermissionsAsync();
223
+ let finalStatus = existingStatus;
224
+
225
+ if (existingStatus !== 'granted') {
226
+ const { status } = await Notifications.requestPermissionsAsync();
227
+ finalStatus = status;
228
+ }
229
+
230
+ if (finalStatus !== 'granted') {
231
+ return null;
232
+ }
233
+
234
+ // Android channel
235
+ if (Platform.OS === 'android') {
236
+ await Notifications.setNotificationChannelAsync('default', {
237
+ name: 'Default',
238
+ importance: Notifications.AndroidImportance.MAX,
239
+ vibrationPattern: [0, 250, 250, 250],
240
+ });
241
+ }
242
+
243
+ const token = await Notifications.getExpoPushTokenAsync({
244
+ projectId: 'your-project-id',
245
+ });
246
+
247
+ return token.data;
248
+ }
249
+ ```
250
+
251
+ ### NATIVE-008: Handle notifications
252
+
253
+ ```typescript
254
+ import * as Notifications from 'expo-notifications';
255
+ import { router } from 'expo-router';
256
+
257
+ function useNotificationHandlers() {
258
+ useEffect(() => {
259
+ // Notification received while app is foregrounded
260
+ const foregroundSubscription = Notifications.addNotificationReceivedListener(
261
+ (notification) => {
262
+ console.log('Notification received:', notification);
263
+ }
264
+ );
265
+
266
+ // User tapped notification
267
+ const responseSubscription = Notifications.addNotificationResponseReceivedListener(
268
+ (response) => {
269
+ const data = response.notification.request.content.data;
270
+
271
+ // Navigate based on notification data
272
+ if (data.screen) {
273
+ router.push(data.screen as string);
274
+ }
275
+ }
276
+ );
277
+
278
+ return () => {
279
+ foregroundSubscription.remove();
280
+ responseSubscription.remove();
281
+ };
282
+ }, []);
283
+ }
284
+ ```
285
+
286
+ ---
287
+
288
+ ## Haptics
289
+
290
+ ### NATIVE-009: Haptic feedback
291
+
292
+ ```typescript
293
+ import * as Haptics from 'expo-haptics';
294
+
295
+ // Light - selections, toggles
296
+ function handleSelect() {
297
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
298
+ }
299
+
300
+ // Medium - button presses
301
+ function handlePress() {
302
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
303
+ }
304
+
305
+ // Heavy - significant actions
306
+ function handleDrop() {
307
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
308
+ }
309
+
310
+ // Success/error feedback
311
+ function handleSuccess() {
312
+ Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
313
+ }
314
+
315
+ function handleError() {
316
+ Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
317
+ }
318
+
319
+ // Selection changed (subtle)
320
+ function handleSelectionChange() {
321
+ Haptics.selectionAsync();
322
+ }
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Biometrics
328
+
329
+ ### NATIVE-010: Biometric authentication
330
+
331
+ ```typescript
332
+ import * as LocalAuthentication from 'expo-local-authentication';
333
+
334
+ async function authenticateWithBiometrics() {
335
+ // Check hardware support
336
+ const hasHardware = await LocalAuthentication.hasHardwareAsync();
337
+ if (!hasHardware) {
338
+ return { success: false, error: 'No biometric hardware' };
339
+ }
340
+
341
+ // Check enrollment
342
+ const isEnrolled = await LocalAuthentication.isEnrolledAsync();
343
+ if (!isEnrolled) {
344
+ return { success: false, error: 'No biometrics enrolled' };
345
+ }
346
+
347
+ // Authenticate
348
+ const result = await LocalAuthentication.authenticateAsync({
349
+ promptMessage: 'Authenticate to continue',
350
+ cancelLabel: 'Cancel',
351
+ disableDeviceFallback: false, // Allow PIN fallback
352
+ });
353
+
354
+ return { success: result.success, error: result.error };
355
+ }
356
+ ```
357
+
358
+ ---
359
+
360
+ ## Share & Clipboard
361
+
362
+ ### NATIVE-011: Share content
363
+
364
+ ```typescript
365
+ import { Share } from 'react-native';
366
+
367
+ async function shareContent(title: string, message: string, url?: string) {
368
+ try {
369
+ await Share.share({
370
+ title,
371
+ message: url ? `${message}\n${url}` : message,
372
+ url, // iOS only
373
+ });
374
+ } catch (error) {
375
+ console.error('Share failed:', error);
376
+ }
377
+ }
378
+
379
+ // Usage
380
+ shareContent('Check this out!', 'Amazing article', 'https://example.com');
381
+ ```
382
+
383
+ ### NATIVE-012: Clipboard
384
+
385
+ ```typescript
386
+ import * as Clipboard from 'expo-clipboard';
387
+
388
+ async function copyToClipboard(text: string) {
389
+ await Clipboard.setStringAsync(text);
390
+ // Show toast/feedback
391
+ }
392
+
393
+ async function pasteFromClipboard(): Promise<string> {
394
+ return Clipboard.getStringAsync();
395
+ }
396
+
397
+ // With haptic feedback
398
+ async function copyWithFeedback(text: string) {
399
+ await Clipboard.setStringAsync(text);
400
+ Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
401
+ Toast.show('Copied!');
402
+ }
403
+ ```
404
+
405
+ ---
406
+
407
+ ## Device Info
408
+
409
+ ### NATIVE-013: Device information
410
+
411
+ ```typescript
412
+ import * as Device from 'expo-device';
413
+ import { Platform } from 'react-native';
414
+
415
+ const deviceInfo = {
416
+ brand: Device.brand,
417
+ modelName: Device.modelName,
418
+ osName: Device.osName,
419
+ osVersion: Device.osVersion,
420
+ isDevice: Device.isDevice,
421
+ platform: Platform.OS,
422
+ };
423
+
424
+ // Check if physical device
425
+ if (!Device.isDevice) {
426
+ console.log('Running in simulator/emulator');
427
+ }
428
+ ```
429
+
430
+ ### NATIVE-014: App state
431
+
432
+ ```typescript
433
+ import { useAppState } from '@react-native-community/hooks';
434
+ import { AppState } from 'react-native';
435
+
436
+ function useAppStateListener(callback: (state: string) => void) {
437
+ useEffect(() => {
438
+ const subscription = AppState.addEventListener('change', callback);
439
+ return () => subscription.remove();
440
+ }, [callback]);
441
+ }
442
+
443
+ // Refresh data when app comes to foreground
444
+ function DataRefresher() {
445
+ const queryClient = useQueryClient();
446
+
447
+ useAppStateListener((state) => {
448
+ if (state === 'active') {
449
+ queryClient.invalidateQueries();
450
+ }
451
+ });
452
+
453
+ return null;
454
+ }
455
+ ```