opencastle 0.33.9 → 0.34.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.
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +39 -17
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/stack-config.d.ts.map +1 -1
- package/dist/cli/stack-config.js +5 -0
- package/dist/cli/stack-config.js.map +1 -1
- package/dist/cli/types.d.ts +1 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/orchestrator/plugins/cloudflare/config.d.ts +3 -0
- package/dist/orchestrator/plugins/cloudflare/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/cloudflare/config.js +23 -0
- package/dist/orchestrator/plugins/cloudflare/config.js.map +1 -0
- package/dist/orchestrator/plugins/coolify/config.d.ts +3 -0
- package/dist/orchestrator/plugins/coolify/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/coolify/config.js +28 -0
- package/dist/orchestrator/plugins/coolify/config.js.map +1 -0
- package/dist/orchestrator/plugins/drizzle/config.d.ts +3 -0
- package/dist/orchestrator/plugins/drizzle/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/drizzle/config.js +15 -0
- package/dist/orchestrator/plugins/drizzle/config.js.map +1 -0
- package/dist/orchestrator/plugins/expo/config.d.ts +3 -0
- package/dist/orchestrator/plugins/expo/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/expo/config.js +23 -0
- package/dist/orchestrator/plugins/expo/config.js.map +1 -0
- package/dist/orchestrator/plugins/index.d.ts.map +1 -1
- package/dist/orchestrator/plugins/index.js +12 -0
- package/dist/orchestrator/plugins/index.js.map +1 -1
- package/dist/orchestrator/plugins/sentry/config.d.ts +3 -0
- package/dist/orchestrator/plugins/sentry/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/sentry/config.js +28 -0
- package/dist/orchestrator/plugins/sentry/config.js.map +1 -0
- package/dist/orchestrator/plugins/stripe/config.d.ts +3 -0
- package/dist/orchestrator/plugins/stripe/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/stripe/config.js +42 -0
- package/dist/orchestrator/plugins/stripe/config.js.map +1 -0
- package/dist/orchestrator/plugins/types.d.ts +1 -1
- package/dist/orchestrator/plugins/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/init.ts +43 -22
- package/src/cli/stack-config.ts +5 -0
- package/src/cli/types.ts +1 -1
- package/src/dashboard/dist/data/convoys/demo-api-v2.json +3 -3
- package/src/dashboard/dist/data/convoys/demo-auth-revamp.json +4 -4
- package/src/dashboard/dist/data/convoys/demo-dashboard-ui.json +12 -12
- package/src/dashboard/dist/data/convoys/demo-data-pipeline.json +3 -3
- package/src/dashboard/dist/data/convoys/demo-deploy-ci.json +1 -1
- package/src/dashboard/dist/data/convoys/demo-docs-update.json +3 -3
- package/src/dashboard/dist/data/convoys/demo-perf-opt.json +4 -4
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/dashboard/public/data/convoys/demo-api-v2.json +3 -3
- package/src/dashboard/public/data/convoys/demo-auth-revamp.json +4 -4
- package/src/dashboard/public/data/convoys/demo-dashboard-ui.json +12 -12
- package/src/dashboard/public/data/convoys/demo-data-pipeline.json +3 -3
- package/src/dashboard/public/data/convoys/demo-deploy-ci.json +1 -1
- package/src/dashboard/public/data/convoys/demo-docs-update.json +3 -3
- package/src/dashboard/public/data/convoys/demo-perf-opt.json +4 -4
- package/src/orchestrator/customizations/agents/skill-matrix.json +24 -4
- package/src/orchestrator/customizations/agents/skill-matrix.md +5 -0
- package/src/orchestrator/plugins/cloudflare/SKILL.md +111 -0
- package/src/orchestrator/plugins/cloudflare/config.ts +24 -0
- package/src/orchestrator/plugins/cloudflare/references/deployment.md +147 -0
- package/src/orchestrator/plugins/cloudflare/references/storage.md +118 -0
- package/src/orchestrator/plugins/cloudflare/references/workers.md +135 -0
- package/src/orchestrator/plugins/convex/SKILL.md +62 -20
- package/src/orchestrator/plugins/convex/references/auth-auth0.md +116 -0
- package/src/orchestrator/plugins/convex/references/auth-clerk.md +113 -0
- package/src/orchestrator/plugins/convex/references/auth-convex-auth.md +143 -0
- package/src/orchestrator/plugins/convex/references/auth-setup.md +87 -0
- package/src/orchestrator/plugins/convex/references/auth-workos.md +114 -0
- package/src/orchestrator/plugins/convex/references/components-advanced.md +134 -0
- package/src/orchestrator/plugins/convex/references/components.md +171 -0
- package/src/orchestrator/plugins/convex/references/function-budget.md +232 -0
- package/src/orchestrator/plugins/convex/references/hot-path-rules.md +371 -0
- package/src/orchestrator/plugins/convex/references/migrations-component.md +170 -0
- package/src/orchestrator/plugins/convex/references/migrations.md +259 -0
- package/src/orchestrator/plugins/convex/references/occ-conflicts.md +126 -0
- package/src/orchestrator/plugins/convex/references/performance-audit.md +80 -0
- package/src/orchestrator/plugins/convex/references/quickstart.md +176 -0
- package/src/orchestrator/plugins/convex/references/subscription-cost.md +252 -0
- package/src/orchestrator/plugins/coolify/SKILL.md +134 -0
- package/src/orchestrator/plugins/coolify/config.ts +29 -0
- package/src/orchestrator/plugins/coolify/references/applications.md +65 -0
- package/src/orchestrator/plugins/coolify/references/ci-cd-webhooks.md +73 -0
- package/src/orchestrator/plugins/coolify/references/databases-services.md +57 -0
- package/src/orchestrator/plugins/coolify/references/docker-compose.md +121 -0
- package/src/orchestrator/plugins/coolify/references/infrastructure.md +77 -0
- package/src/orchestrator/plugins/drizzle/SKILL.md +123 -0
- package/src/orchestrator/plugins/drizzle/config.ts +16 -0
- package/src/orchestrator/plugins/drizzle/references/migrations.md +112 -0
- package/src/orchestrator/plugins/drizzle/references/query-patterns.md +127 -0
- package/src/orchestrator/plugins/drizzle/references/schema-patterns.md +105 -0
- package/src/orchestrator/plugins/expo/SKILL.md +114 -0
- package/src/orchestrator/plugins/expo/config.ts +24 -0
- package/src/orchestrator/plugins/expo/references/eas-build.md +73 -0
- package/src/orchestrator/plugins/expo/references/native-modules.md +71 -0
- package/src/orchestrator/plugins/expo/references/routing.md +83 -0
- package/src/orchestrator/plugins/index.ts +12 -0
- package/src/orchestrator/plugins/linear/SKILL.md +21 -3
- package/src/orchestrator/plugins/sentry/SKILL.md +94 -0
- package/src/orchestrator/plugins/sentry/config.ts +29 -0
- package/src/orchestrator/plugins/sentry/references/error-patterns.md +112 -0
- package/src/orchestrator/plugins/sentry/references/performance.md +66 -0
- package/src/orchestrator/plugins/sentry/references/sdk-setup.md +108 -0
- package/src/orchestrator/plugins/stripe/SKILL.md +138 -0
- package/src/orchestrator/plugins/stripe/config.ts +43 -0
- package/src/orchestrator/plugins/stripe/references/api-patterns.md +57 -0
- package/src/orchestrator/plugins/stripe/references/projects-setup.md +30 -0
- package/src/orchestrator/plugins/stripe/references/upgrade-guide.md +105 -0
- package/src/orchestrator/plugins/types.ts +1 -1
- package/src/orchestrator/skills/backbone-scaffolding/EXAMPLES.md +1 -1
- package/src/orchestrator/skills/backbone-scaffolding/SKILL.md +32 -16
- package/src/orchestrator/plugins/convex/REFERENCE.md +0 -9
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: expo-development
|
|
3
|
+
description: "Scaffolds Expo/React Native apps, configures EAS Build profiles, manages native modules via CNG, sets up Expo Router navigation, and uses EAS Update for OTA deployments. Use when creating React Native apps with Expo, configuring EAS builds, setting up Expo Router, managing native modules, or deploying OTA updates."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Expo Development
|
|
7
|
+
|
|
8
|
+
## Topic Routing
|
|
9
|
+
|
|
10
|
+
Read the matching reference before writing code for any of these topics:
|
|
11
|
+
|
|
12
|
+
| Topic | Reference |
|
|
13
|
+
|-------|-----------|
|
|
14
|
+
| EAS Build & deployment | `references/eas-build.md` |
|
|
15
|
+
| Expo Router & navigation | `references/routing.md` |
|
|
16
|
+
| Native modules & CNG | `references/native-modules.md` |
|
|
17
|
+
|
|
18
|
+
## Critical Rules
|
|
19
|
+
|
|
20
|
+
**Project Setup**
|
|
21
|
+
- Use `npx create-expo-app@latest` for new projects — always target the latest SDK
|
|
22
|
+
- Use Continuous Native Generation (CNG) — never eject; prefer `npx expo prebuild` when native config is needed
|
|
23
|
+
- Configure `app.json` / `app.config.ts` for all project metadata — never modify native projects directly when using CNG
|
|
24
|
+
|
|
25
|
+
**Expo Router**
|
|
26
|
+
- File-based routing in `app/` — define `_layout.tsx` in each directory for `<Stack>`, `<Tabs>`, or `<Drawer>`
|
|
27
|
+
- Use `Link` for declarative navigation, `useRouter()` for imperative — configure `scheme` in app.json for deep links
|
|
28
|
+
|
|
29
|
+
**EAS Build**
|
|
30
|
+
- Define `development` / `preview` / `production` profiles in `eas.json`
|
|
31
|
+
- Preview: `"distribution": "internal"` + `"channel": "preview"` — Development: add `"developmentClient": true`
|
|
32
|
+
- Production: `"channel": "production"` + `"autoIncrement": true`
|
|
33
|
+
|
|
34
|
+
**EAS Update (OTA)**
|
|
35
|
+
- `eas update --channel production` for JS-only changes — no app store review
|
|
36
|
+
- Set `"runtimeVersion": { "policy": "fingerprint" }` — mismatched runtimeVersion forces a new binary build
|
|
37
|
+
|
|
38
|
+
**Native Modules**
|
|
39
|
+
- Prefer Expo SDK packages over community alternatives — install with `npx expo install` for version resolution
|
|
40
|
+
- Config plugins (`app.plugin.js`) modify native projects at prebuild time — never edit `ios/` or `android/` directly
|
|
41
|
+
- Custom native code: `npx create-expo-module` scaffolds Swift + Kotlin module
|
|
42
|
+
|
|
43
|
+
**Security**
|
|
44
|
+
- Store API keys and secrets in EAS Secrets — never hardcode in `app.json` or source code
|
|
45
|
+
- Use `expo-secure-store` for device-side secret storage
|
|
46
|
+
|
|
47
|
+
## Workflow: New Project → Build → Deploy
|
|
48
|
+
|
|
49
|
+
1. **Scaffold** — `npx create-expo-app@latest my-app --template default@sdk-55`
|
|
50
|
+
2. **Verify local** — `cd my-app && npx expo start` → confirm app loads in simulator
|
|
51
|
+
3. **Init EAS** — `eas init && eas build:configure` → creates `eas.json` with profiles
|
|
52
|
+
4. **Build preview** — `eas build --profile preview --platform ios`
|
|
53
|
+
5. **Check status** — `eas build:list` → wait for `finished` status; if `errored` → `eas build:view <id>` → fix config → rebuild
|
|
54
|
+
6. **Test** — install preview build on device via QR code or TestFlight
|
|
55
|
+
7. **Build production** — `eas build --profile production --platform all`
|
|
56
|
+
8. **Submit** — `eas submit --platform ios && eas submit --platform android`
|
|
57
|
+
|
|
58
|
+
## Workflow: OTA Update
|
|
59
|
+
|
|
60
|
+
1. **Make JS-only changes** — modify components, styles, or logic (no native module changes)
|
|
61
|
+
2. **Deploy** — `eas update --channel production --message "Fix: button alignment"`
|
|
62
|
+
3. **Verify** — `eas update:list` → confirm update published with correct `runtimeVersion`
|
|
63
|
+
4. **Rollback if needed** — `eas update:republish --group <previous-group-id>`
|
|
64
|
+
|
|
65
|
+
> If `runtimeVersion` mismatch: a new binary build is required — OTA cannot bridge native changes.
|
|
66
|
+
|
|
67
|
+
## Build Failure Recovery
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Build failed → check logs
|
|
71
|
+
├── "Provisioning profile" error → eas credentials → fix iOS signing
|
|
72
|
+
├── "SDK version mismatch" → npx expo install --fix → rebuild
|
|
73
|
+
├── "Metro bundler" error → check JS syntax → npx expo start to reproduce
|
|
74
|
+
├── "Native module" error → npx expo prebuild --clean → verify config plugins
|
|
75
|
+
└── "EAS secret" missing → eas secret:create → rebuild
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Screen with Expo Router
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
// app/user/[id].tsx
|
|
82
|
+
import { useLocalSearchParams } from 'expo-router';
|
|
83
|
+
import { View, Text } from 'react-native';
|
|
84
|
+
|
|
85
|
+
export default function UserScreen() {
|
|
86
|
+
const { id } = useLocalSearchParams<{ id: string }>();
|
|
87
|
+
return (
|
|
88
|
+
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
|
89
|
+
<Text>User {id}</Text>
|
|
90
|
+
</View>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## EAS Build Profile
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"build": {
|
|
100
|
+
"development": {
|
|
101
|
+
"developmentClient": true,
|
|
102
|
+
"distribution": "internal"
|
|
103
|
+
},
|
|
104
|
+
"preview": {
|
|
105
|
+
"distribution": "internal",
|
|
106
|
+
"channel": "preview"
|
|
107
|
+
},
|
|
108
|
+
"production": {
|
|
109
|
+
"channel": "production",
|
|
110
|
+
"autoIncrement": true
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { PluginConfig } from '../types.js';
|
|
2
|
+
|
|
3
|
+
export const config: PluginConfig = {
|
|
4
|
+
id: 'expo',
|
|
5
|
+
name: 'Expo',
|
|
6
|
+
category: 'tech',
|
|
7
|
+
subCategory: 'framework',
|
|
8
|
+
label: 'Expo',
|
|
9
|
+
hint: 'React Native framework with EAS builds and OTA updates',
|
|
10
|
+
skillName: 'expo-development',
|
|
11
|
+
mcpServerKey: 'Expo',
|
|
12
|
+
mcpConfig: {
|
|
13
|
+
type: 'http',
|
|
14
|
+
url: 'https://mcp.expo.dev/mcp',
|
|
15
|
+
},
|
|
16
|
+
authType: 'oauth',
|
|
17
|
+
envVars: [],
|
|
18
|
+
agentToolMap: {
|
|
19
|
+
'developer': ['search_documentation', 'read_documentation', 'add_library'],
|
|
20
|
+
'testing-expert': ['automation_take_screenshot', 'automation_tap', 'automation_find_view'],
|
|
21
|
+
},
|
|
22
|
+
docsUrl: null,
|
|
23
|
+
officialDocs: 'https://docs.expo.dev/',
|
|
24
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# EAS Build & Deployment
|
|
2
|
+
|
|
3
|
+
## Build Profiles
|
|
4
|
+
|
|
5
|
+
Define in `eas.json` at project root:
|
|
6
|
+
|
|
7
|
+
- **development** — installs `expo-dev-client`, `distribution: internal`
|
|
8
|
+
- **preview** — for TestFlight/internal testing, `distribution: internal`
|
|
9
|
+
- **production** — for app store submission, `autoIncrement: true`
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Build for iOS
|
|
15
|
+
eas build --platform ios --profile preview
|
|
16
|
+
|
|
17
|
+
# Build for Android
|
|
18
|
+
eas build --platform android --profile preview
|
|
19
|
+
|
|
20
|
+
# Submit to App Store
|
|
21
|
+
eas submit --platform ios
|
|
22
|
+
|
|
23
|
+
# Submit to Google Play
|
|
24
|
+
eas submit --platform android
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## EAS Update (OTA)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Deploy JS-only update
|
|
31
|
+
eas update --channel production --message "Fix button alignment"
|
|
32
|
+
|
|
33
|
+
# Check update status
|
|
34
|
+
eas update:list
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Runtime Version Policy
|
|
38
|
+
|
|
39
|
+
Use fingerprint policy in `app.json`:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"expo": {
|
|
44
|
+
"runtimeVersion": {
|
|
45
|
+
"policy": "fingerprint"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This auto-detects binary-incompatible changes and forces a new build.
|
|
52
|
+
|
|
53
|
+
## EAS Workflows
|
|
54
|
+
|
|
55
|
+
Define CI/CD in `.eas/workflows/`:
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
name: build-and-deploy
|
|
59
|
+
on:
|
|
60
|
+
push:
|
|
61
|
+
branches: [main]
|
|
62
|
+
jobs:
|
|
63
|
+
build:
|
|
64
|
+
type: build
|
|
65
|
+
params:
|
|
66
|
+
platform: ios
|
|
67
|
+
profile: production
|
|
68
|
+
submit:
|
|
69
|
+
type: submit
|
|
70
|
+
needs: [build]
|
|
71
|
+
params:
|
|
72
|
+
platform: ios
|
|
73
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Native Modules & CNG
|
|
2
|
+
|
|
3
|
+
## Continuous Native Generation (CNG)
|
|
4
|
+
|
|
5
|
+
CNG regenerates native projects from `app.json` config:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Generate native projects (don't commit these)
|
|
9
|
+
npx expo prebuild
|
|
10
|
+
|
|
11
|
+
# Clean and regenerate
|
|
12
|
+
npx expo prebuild --clean
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Add `ios/` and `android/` to `.gitignore` when using CNG.
|
|
16
|
+
|
|
17
|
+
## Installing Expo SDK Packages
|
|
18
|
+
|
|
19
|
+
Always use `npx expo install` — it resolves compatible versions:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx expo install expo-camera expo-location expo-notifications
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Config Plugins
|
|
26
|
+
|
|
27
|
+
Modify native projects at prebuild time without manual edits:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
// app.plugin.js
|
|
31
|
+
const { withInfoPlist } = require('@expo/config-plugins');
|
|
32
|
+
|
|
33
|
+
module.exports = function myPlugin(config) {
|
|
34
|
+
return withInfoPlist(config, (config) => {
|
|
35
|
+
config.modResults.NSCameraUsageDescription = 'App needs camera access';
|
|
36
|
+
return config;
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Register in `app.json`:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"expo": {
|
|
46
|
+
"plugins": ["./app.plugin.js"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Creating Custom Modules
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx create-expo-module my-native-module
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This scaffolds a module with iOS (Swift) and Android (Kotlin) native code.
|
|
58
|
+
|
|
59
|
+
## Expo SDK Package Selection
|
|
60
|
+
|
|
61
|
+
| Need | Package |
|
|
62
|
+
|------|---------|
|
|
63
|
+
| Camera | `expo-camera` |
|
|
64
|
+
| Location | `expo-location` |
|
|
65
|
+
| Push notifications | `expo-notifications` |
|
|
66
|
+
| File system | `expo-file-system` |
|
|
67
|
+
| Secure storage | `expo-secure-store` |
|
|
68
|
+
| Auth | `expo-auth-session` |
|
|
69
|
+
| SQLite | `expo-sqlite` |
|
|
70
|
+
| Haptics | `expo-haptics` |
|
|
71
|
+
| Image picker | `expo-image-picker` |
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Expo Router & Navigation
|
|
2
|
+
|
|
3
|
+
## File-Based Routing
|
|
4
|
+
|
|
5
|
+
Routes map to files in the `app/` directory:
|
|
6
|
+
|
|
7
|
+
| File | Route |
|
|
8
|
+
|------|-------|
|
|
9
|
+
| `app/index.tsx` | `/` |
|
|
10
|
+
| `app/about.tsx` | `/about` |
|
|
11
|
+
| `app/user/[id].tsx` | `/user/:id` |
|
|
12
|
+
| `app/(tabs)/_layout.tsx` | Tab navigator |
|
|
13
|
+
| `app/(auth)/login.tsx` | `/login` (grouped) |
|
|
14
|
+
|
|
15
|
+
## Layout Files
|
|
16
|
+
|
|
17
|
+
Every directory can have a `_layout.tsx` that wraps its children:
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { Stack } from 'expo-router';
|
|
21
|
+
|
|
22
|
+
export default function RootLayout() {
|
|
23
|
+
return (
|
|
24
|
+
<Stack>
|
|
25
|
+
<Stack.Screen name="index" options={{ title: 'Home' }} />
|
|
26
|
+
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
|
27
|
+
</Stack>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Navigation
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { Link, useRouter } from 'expo-router';
|
|
36
|
+
|
|
37
|
+
// Declarative
|
|
38
|
+
<Link href="/user/123">View Profile</Link>
|
|
39
|
+
|
|
40
|
+
// Imperative
|
|
41
|
+
const router = useRouter();
|
|
42
|
+
router.push('/user/123');
|
|
43
|
+
router.replace('/home');
|
|
44
|
+
router.back();
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Deep Linking
|
|
48
|
+
|
|
49
|
+
Configure in `app.json`:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"expo": {
|
|
54
|
+
"scheme": "myapp",
|
|
55
|
+
"web": {
|
|
56
|
+
"bundler": "metro"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
URLs like `myapp://user/123` automatically resolve to `app/user/[id].tsx`.
|
|
63
|
+
|
|
64
|
+
## Tab Navigation
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { Tabs } from 'expo-router';
|
|
68
|
+
import { Ionicons } from '@expo/vector-icons';
|
|
69
|
+
|
|
70
|
+
export default function TabLayout() {
|
|
71
|
+
return (
|
|
72
|
+
<Tabs>
|
|
73
|
+
<Tabs.Screen
|
|
74
|
+
name="index"
|
|
75
|
+
options={{
|
|
76
|
+
title: 'Home',
|
|
77
|
+
tabBarIcon: ({ color }) => <Ionicons name="home" color={color} size={24} />,
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
</Tabs>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
@@ -21,8 +21,14 @@ import { config as playwright } from './playwright/config.js';
|
|
|
21
21
|
import { config as vitest } from './vitest/config.js';
|
|
22
22
|
import { config as figma } from './figma/config.js';
|
|
23
23
|
import { config as resend } from './resend/config.js';
|
|
24
|
+
import { config as stripe } from './stripe/config.js';
|
|
24
25
|
import { config as nextjs } from './nextjs/config.js';
|
|
25
26
|
import { config as astro } from './astro/config.js';
|
|
27
|
+
import { config as sentry } from './sentry/config.js';
|
|
28
|
+
import { config as drizzle } from './drizzle/config.js';
|
|
29
|
+
import { config as cloudflare } from './cloudflare/config.js';
|
|
30
|
+
import { config as coolify } from './coolify/config.js';
|
|
31
|
+
import { config as expo } from './expo/config.js';
|
|
26
32
|
|
|
27
33
|
export type { PluginConfig, McpServerConfig, McpInput, EnvVarRequirement } from './types.js';
|
|
28
34
|
|
|
@@ -44,8 +50,14 @@ export const PLUGINS: Record<string, PluginConfig> = {
|
|
|
44
50
|
vitest,
|
|
45
51
|
figma,
|
|
46
52
|
resend,
|
|
53
|
+
stripe,
|
|
47
54
|
nextjs,
|
|
48
55
|
astro,
|
|
56
|
+
sentry,
|
|
57
|
+
drizzle,
|
|
58
|
+
cloudflare,
|
|
59
|
+
coolify,
|
|
60
|
+
expo,
|
|
49
61
|
linear,
|
|
50
62
|
jira,
|
|
51
63
|
trello,
|
|
@@ -7,7 +7,7 @@ description: "Creates and names Linear issues, assigns labels and priorities, ma
|
|
|
7
7
|
|
|
8
8
|
# Task Management with Linear
|
|
9
9
|
|
|
10
|
-
For project-specific team ID, workflow state UUIDs, and label UUIDs, see [tracker-config.md](../../.opencastle/project/tracker-config.md).
|
|
10
|
+
For project-specific team ID, workflow state UUIDs, and label UUIDs, see [tracker-config.md](../../.opencastle/project/tracker-config.md). For status transitions, follow the **Status Update Procedure** section below.
|
|
11
11
|
|
|
12
12
|
## MCP Tool Examples
|
|
13
13
|
|
|
@@ -16,8 +16,8 @@ For project-specific team ID, workflow state UUIDs, and label UUIDs, see [tracke
|
|
|
16
16
|
{ "teamId": "TEAM_UUID", "title": "[UI] Build PriceRangeFilter", "description": "Objective: ...\nFiles: ...\nAC: ...", "labelIds": ["LABEL_UUID"], "priority": 2 }
|
|
17
17
|
// → { "id": "TAS-42", "url": "https://linear.app/team/TAS-42" }
|
|
18
18
|
|
|
19
|
-
// Update issue status
|
|
20
|
-
{ "issueId": "TAS-42", "stateId": "
|
|
19
|
+
// Update issue status (see Status Update Procedure)
|
|
20
|
+
{ "issueId": "TAS-42", "stateId": "<UUID from tracker-config.md>" }
|
|
21
21
|
|
|
22
22
|
// Search issues
|
|
23
23
|
{ "query": "is:open assignee:me", "teamId": "TEAM_UUID" }
|
|
@@ -76,3 +76,21 @@ Group related issues under a Linear project; issues track individual subtasks.
|
|
|
76
76
|
6. On completion: verify all issues Done/Cancelled, run build/lint/test.
|
|
77
77
|
|
|
78
78
|
If creation fails: check team ID and state UUIDs in [tracker-config.md](../../.opencastle/project/tracker-config.md); retry once. If board state is inconsistent on resume: re-read all issue statuses before proceeding.
|
|
79
|
+
|
|
80
|
+
## Status Update Procedure
|
|
81
|
+
|
|
82
|
+
> ⚠️ `update_issue` requires a workflow state **UUID** — passing a name like `"In Progress"` always fails with `stateId must be a UUID`.
|
|
83
|
+
|
|
84
|
+
1. **Read UUIDs from [tracker-config.md](../../.opencastle/project/tracker-config.md)** before any status transition. Names returned by `list_issues`/`get_issue` are display-only, not valid `stateId` values.
|
|
85
|
+
2. **If `tracker-config.md` has no state UUIDs**, skip status updates and log a warning.
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
// ✅ UUID from tracker-config.md
|
|
89
|
+
{ "issueId": "TAS-42", "stateId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }
|
|
90
|
+
// ❌ FAILS — name, not UUID
|
|
91
|
+
{ "issueId": "TAS-42", "status": "In Progress" }
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## One-Time Setup: Discover Workflow State UUIDs
|
|
95
|
+
|
|
96
|
+
If `tracker-config.md` lacks state UUIDs, ask the user to populate them from Linear Settings → Teams → Workflow (UUID in browser URL per state) or via the GraphQL `workflowStates { nodes { id name } }` query.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sentry-monitoring
|
|
3
|
+
description: "Sentry error monitoring, SDK initialization, performance tracing, source maps, session replay, and release tracking. Use when adding Sentry to a project, capturing errors with context, setting up distributed tracing, configuring source maps, or debugging production issues."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Sentry Monitoring
|
|
7
|
+
|
|
8
|
+
## Topic Routing
|
|
9
|
+
|
|
10
|
+
Read the matching reference before writing code for any of these topics:
|
|
11
|
+
|
|
12
|
+
| Topic | Reference |
|
|
13
|
+
|-------|-----------|
|
|
14
|
+
| SDK setup & initialization | `references/sdk-setup.md` |
|
|
15
|
+
| Error capture & context enrichment | `references/error-patterns.md` |
|
|
16
|
+
| Performance tracing & spans | `references/performance.md` |
|
|
17
|
+
|
|
18
|
+
## Critical Rules
|
|
19
|
+
|
|
20
|
+
**SDK Initialization**
|
|
21
|
+
- Next.js requires separate init files: `instrumentation-client.ts` (browser), `sentry.server.config.ts` (Node), `sentry.edge.config.ts` (edge runtime)
|
|
22
|
+
- Call `Sentry.init()` as early as possible — before any other imports that might throw
|
|
23
|
+
- Set `sendDefaultPii: true` to capture authenticated user context automatically
|
|
24
|
+
- Use `npx @sentry/wizard@latest -i nextjs` for guided setup; it creates all init files and patches `next.config.ts`
|
|
25
|
+
|
|
26
|
+
**Error Capture**
|
|
27
|
+
- Use `Sentry.captureException(err)` for caught errors; uncaught errors are captured automatically
|
|
28
|
+
- Enrich errors with `Sentry.setContext()`, `Sentry.setTag()`, `Sentry.setUser()` before or inside the capture call
|
|
29
|
+
- Add breadcrumbs with `Sentry.addBreadcrumb()` to build a debugging trail leading to the error
|
|
30
|
+
- Override fingerprinting via `event.fingerprint` in `beforeSend` to control issue grouping
|
|
31
|
+
|
|
32
|
+
**Performance**
|
|
33
|
+
- Set `tracesSampleRate: 1.0` in development, `0.1` (or lower) in production to control costs
|
|
34
|
+
- Use `Sentry.startSpan()` for custom instrumentation around expensive operations
|
|
35
|
+
- Core Web Vitals are captured automatically by the browser SDK — no extra config needed
|
|
36
|
+
|
|
37
|
+
**Source Maps**
|
|
38
|
+
- Wrap `next.config.ts` with `withSentryConfig()` to auto-upload source maps at build time
|
|
39
|
+
- Set `SENTRY_AUTH_TOKEN` in CI environment; generate at Organization Settings → Auth Tokens
|
|
40
|
+
- Enable `tunnelRoute: '/monitoring'` to proxy Sentry requests through your server and avoid ad-blockers
|
|
41
|
+
|
|
42
|
+
**Releases & Replay**
|
|
43
|
+
- Associate commits by setting `SENTRY_RELEASE` or letting the wizard configure it automatically
|
|
44
|
+
- Enable Session Replay with `replaysSessionSampleRate` and `replaysOnErrorSampleRate` for visual debugging
|
|
45
|
+
- Never expose the DSN in server-side code that handles secrets — DSN is safe for client-side use only
|
|
46
|
+
|
|
47
|
+
## Next.js Init Pattern
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// instrumentation-client.ts (browser) — mirrors sentry.server.config.ts / sentry.edge.config.ts
|
|
51
|
+
import * as Sentry from '@sentry/nextjs';
|
|
52
|
+
|
|
53
|
+
Sentry.init({
|
|
54
|
+
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
55
|
+
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
|
|
56
|
+
sendDefaultPii: true,
|
|
57
|
+
// Browser-only options:
|
|
58
|
+
replaysSessionSampleRate: 0.1,
|
|
59
|
+
replaysOnErrorSampleRate: 1.0,
|
|
60
|
+
integrations: [Sentry.replayIntegration()],
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Capture with Context
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import * as Sentry from '@sentry/nextjs';
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
await processOrder(orderId);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
Sentry.withScope((scope) => {
|
|
73
|
+
scope.setTag('action', 'process-order');
|
|
74
|
+
scope.setContext('order', { orderId, userId });
|
|
75
|
+
Sentry.captureException(err);
|
|
76
|
+
});
|
|
77
|
+
throw err; // re-throw after capturing
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Reference Files
|
|
82
|
+
|
|
83
|
+
- `references/sdk-setup.md` — Init patterns for Next.js, React, Node.js; wizard usage; config options
|
|
84
|
+
- `references/error-patterns.md` — captureException, context enrichment, breadcrumbs, fingerprinting, error boundaries
|
|
85
|
+
- `references/performance.md` — tracesSampleRate, startSpan, distributed tracing, custom instrumentation
|
|
86
|
+
|
|
87
|
+
## Quick Workflow: Add Sentry to a Next.js app
|
|
88
|
+
1. Run `npx @sentry/wizard@latest -i nextjs` — select your Sentry org and project when prompted
|
|
89
|
+
2. Wizard creates `instrumentation-client.ts`, `sentry.server.config.ts`, `sentry.edge.config.ts`, and patches `next.config.ts`
|
|
90
|
+
3. Set `NEXT_PUBLIC_SENTRY_DSN` in `.env.local` and `SENTRY_AUTH_TOKEN` in `.env.local` (and CI secrets)
|
|
91
|
+
4. Adjust `tracesSampleRate` in each init file — verify Sentry receives a test event before merging
|
|
92
|
+
5. Add `global-error.tsx` and call `Sentry.captureException` inside it for App Router error boundaries
|
|
93
|
+
- **If events not appearing:** check DSN matches the project → verify `tunnelRoute` is configured → confirm `SENTRY_AUTH_TOKEN` is set for source map uploads
|
|
94
|
+
- **If source maps missing:** confirm `withSentryConfig` wraps the Next.js config → check build logs for upload errors
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PluginConfig } from '../types.js';
|
|
2
|
+
|
|
3
|
+
export const config: PluginConfig = {
|
|
4
|
+
id: 'sentry',
|
|
5
|
+
name: 'Sentry',
|
|
6
|
+
category: 'tech',
|
|
7
|
+
subCategory: 'observability',
|
|
8
|
+
label: 'Sentry',
|
|
9
|
+
hint: 'Error monitoring, performance tracing, and observability',
|
|
10
|
+
skillName: 'sentry-monitoring',
|
|
11
|
+
mcpServerKey: 'Sentry',
|
|
12
|
+
mcpConfig: {
|
|
13
|
+
type: 'stdio',
|
|
14
|
+
command: 'npx',
|
|
15
|
+
args: ['@sentry/mcp-server@latest'],
|
|
16
|
+
},
|
|
17
|
+
authType: 'env-token',
|
|
18
|
+
envVars: [
|
|
19
|
+
{ name: 'SENTRY_ACCESS_TOKEN', hint: 'Create at Settings \u2192 Auth Tokens in sentry.io' },
|
|
20
|
+
],
|
|
21
|
+
agentToolMap: {
|
|
22
|
+
'developer': ['search_errors', 'get_issue_details', 'search_issues', 'create_sentry_issue'],
|
|
23
|
+
'devops-expert': ['list_projects', 'get_project_stats', 'search_errors'],
|
|
24
|
+
'security-expert': ['search_errors', 'get_issue_details', 'list_projects'],
|
|
25
|
+
},
|
|
26
|
+
docsUrl: null,
|
|
27
|
+
officialDocs: 'https://docs.sentry.io/',
|
|
28
|
+
mcpPackage: '@sentry/mcp-server',
|
|
29
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Sentry Error Patterns
|
|
2
|
+
|
|
3
|
+
## Basic Capture
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import * as Sentry from '@sentry/nextjs';
|
|
7
|
+
|
|
8
|
+
// Caught error
|
|
9
|
+
try {
|
|
10
|
+
await riskyOperation();
|
|
11
|
+
} catch (err) {
|
|
12
|
+
Sentry.captureException(err);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Custom message
|
|
16
|
+
Sentry.captureMessage('Payment declined', 'warning');
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Context Enrichment
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// Set user (persists for subsequent events in the session)
|
|
23
|
+
Sentry.setUser({ id: user.id, email: user.email });
|
|
24
|
+
|
|
25
|
+
// Structured context object
|
|
26
|
+
Sentry.setContext('order', { orderId, amount, currency });
|
|
27
|
+
|
|
28
|
+
// Searchable tag
|
|
29
|
+
Sentry.setTag('tenant', organizationSlug);
|
|
30
|
+
|
|
31
|
+
// Scoped enrichment (one-off, does not persist)
|
|
32
|
+
Sentry.withScope((scope) => {
|
|
33
|
+
scope.setTag('action', 'checkout');
|
|
34
|
+
scope.setContext('cart', { itemCount, total });
|
|
35
|
+
scope.setLevel('fatal');
|
|
36
|
+
Sentry.captureException(err);
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Breadcrumbs
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
Sentry.addBreadcrumb({
|
|
44
|
+
category: 'payment',
|
|
45
|
+
message: 'User clicked pay button',
|
|
46
|
+
level: 'info',
|
|
47
|
+
data: { amount: 99.99 },
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Custom Fingerprinting
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// sentry.client.config.ts
|
|
55
|
+
Sentry.init({
|
|
56
|
+
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
57
|
+
beforeSend(event) {
|
|
58
|
+
// Group all DB timeout errors together regardless of query
|
|
59
|
+
if (event.exception?.values?.[0]?.value?.includes('timeout')) {
|
|
60
|
+
event.fingerprint = ['database-timeout'];
|
|
61
|
+
}
|
|
62
|
+
return event;
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Error Boundaries (React / Next.js App Router)
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// app/global-error.tsx
|
|
71
|
+
'use client';
|
|
72
|
+
import * as Sentry from '@sentry/nextjs';
|
|
73
|
+
import { useEffect } from 'react';
|
|
74
|
+
|
|
75
|
+
export default function GlobalError({ error, reset }: { error: Error; reset: () => void }) {
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
Sentry.captureException(error);
|
|
78
|
+
}, [error]);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<html>
|
|
82
|
+
<body>
|
|
83
|
+
<h2>Something went wrong</h2>
|
|
84
|
+
<button onClick={reset}>Try again</button>
|
|
85
|
+
</body>
|
|
86
|
+
</html>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Wrapping components
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import * as Sentry from '@sentry/react';
|
|
95
|
+
|
|
96
|
+
export const SafeWidget = Sentry.withErrorBoundary(MyWidget, {
|
|
97
|
+
fallback: <p>This widget failed to load</p>,
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Server Action Instrumentation
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
'use server';
|
|
105
|
+
import * as Sentry from '@sentry/nextjs';
|
|
106
|
+
|
|
107
|
+
export async function submitForm(data: FormData) {
|
|
108
|
+
return await Sentry.withServerActionInstrumentation('submitForm', async () => {
|
|
109
|
+
// Your server action logic here
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
```
|