Skip to content

Commit ad7441a

Browse files
Merge pull request #15 from CarlosZiegler/feat/add-agent-skills
Feat/add agent skills
2 parents b9dcd92 + 72e46cd commit ad7441a

6 files changed

Lines changed: 858 additions & 0 deletions

File tree

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ A highly customizable OTA (Over-The-Air) update UI package for Expo apps. Includ
99

1010
**[📚 Full Documentation](https://ddedic.github.io/expo-fancy-ota-updates/)** | **[📦 npm Package](https://www.npmjs.com/package/@ddedic/expo-fancy-ota-updates)** | **[💻 GitHub](https://github.com/ddedic/expo-fancy-ota-updates)**
1111

12+
## 🤖 Agent Skills
13+
14+
This repository includes reusable Agent Skills for AI coding agents, compatible with the open `SKILL.md` ecosystem used by tools such as [skills.sh](https://www.skills.sh/).
15+
16+
```bash
17+
npx skills add ddedic/expo-fancy-ota-updates --list
18+
npx skills add ddedic/expo-fancy-ota-updates --skill expo-ota-ui-integration
19+
```
20+
21+
See [`skills/README.md`](./skills/README.md) for the included OTA integration, publishing, channel switching, and branding/i18n skills.
1222

1323
---
1424

skills/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Agent Skills for expo-fancy-ota-updates
2+
3+
This directory contains reusable Agent Skills for working with `@ddedic/expo-fancy-ota-updates` in Expo apps.
4+
5+
They follow the same folder-based `SKILL.md` structure used by the open agent skills ecosystem and directories such as [skills.sh](https://www.skills.sh/):
6+
7+
```text
8+
skills/
9+
<skill-name>/
10+
SKILL.md
11+
```
12+
13+
Each `SKILL.md` has YAML frontmatter with a `name` and `description`, followed by task-specific instructions and examples.
14+
15+
## Included skills
16+
17+
- `expo-ota-ui-integration` — integrate `OTAUpdatesProvider`, `UpdateBanner`, `OTAInfoScreen`, `useOTAUpdates`, and `ota-version.json` into an Expo app.
18+
- `expo-ota-publish-workflow` — configure and operate the `ota-publish` CLI for dry runs, publishing, promotion, and reverts.
19+
- `expo-ota-channel-surfing` — implement and troubleshoot runtime channel switching with `switchChannel()`.
20+
- `expo-ota-branding-i18n` — customize themes, translations, render props, and user-facing OTA screens.
21+
22+
## Install with the skills CLI
23+
24+
From another project, list or install these skills from this repository:
25+
26+
```bash
27+
npx skills add ddedic/expo-fancy-ota-updates --list
28+
npx skills add ddedic/expo-fancy-ota-updates --skill expo-ota-ui-integration
29+
npx skills add ddedic/expo-fancy-ota-updates --skill expo-ota-publish-workflow
30+
```
31+
32+
Install all skills:
33+
34+
```bash
35+
npx skills add ddedic/expo-fancy-ota-updates --skill '*'
36+
```
37+
38+
For Claude Code specifically, project installs are typically placed under `.claude/skills/` in the consuming project. The `skills` CLI handles agent-specific install locations.
39+
40+
## Local development
41+
42+
When editing these skills:
43+
44+
1. Keep `name` identical to the parent directory name.
45+
2. Keep descriptions explicit about when an agent should use the skill.
46+
3. Prefer concrete commands and code snippets over abstract guidance.
47+
4. Validate that each `SKILL.md` starts with YAML frontmatter and has a non-empty Markdown body.
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
---
2+
name: expo-ota-branding-i18n
3+
description: Use when customizing expo-fancy-ota-updates with brand themes, localized copy, custom UpdateBanner rendering, or custom OTAInfoScreen sections.
4+
---
5+
6+
# Expo OTA Branding and i18n
7+
8+
## Overview
9+
10+
Use this skill when the default OTA UI needs to match an app's product design, language, or information architecture. `@ddedic/expo-fancy-ota-updates` supports three levels of customization:
11+
12+
1. **Theme tokens** through `OTAUpdatesProvider theme`.
13+
2. **Translations** through `OTAUpdatesProvider translations`.
14+
3. **Render props** for `UpdateBanner` and `OTAInfoScreen` sections.
15+
16+
Start with theme and translations. Use render props only when the layout or component structure must change.
17+
18+
## When to Use
19+
20+
Use this skill when the user asks to:
21+
22+
- Make the update banner match app branding.
23+
- Localize OTA UI text into German, Portuguese, Spanish, French, or another language.
24+
- Replace the default banner with custom UI.
25+
- Hide debug-heavy fields for user-facing settings.
26+
- Customize `OTAInfoScreen` sections with `renderInfo`, `renderActions`, or `renderChangelog`.
27+
28+
Use `expo-ota-ui-integration` first if the provider/banner/info screen are not integrated at all.
29+
30+
## Theme Customization
31+
32+
Define brand tokens and pass them to the provider. Theme values are merged with defaults, but nested `colors` should include every color you rely on for contrast.
33+
34+
```tsx
35+
import { OTAUpdatesProvider } from '@ddedic/expo-fancy-ota-updates';
36+
import versionData from './ota-version.json';
37+
38+
const otaTheme = {
39+
colors: {
40+
primary: '#7C3AED',
41+
primaryLight: '#A78BFA',
42+
background: '#0B0B0F',
43+
backgroundSecondary: '#15151C',
44+
backgroundTertiary: '#222230',
45+
text: '#FFFFFF',
46+
textSecondary: '#D1D5DB',
47+
textTertiary: '#9CA3AF',
48+
border: '#2D2D3A',
49+
error: '#EF4444',
50+
success: '#10B981',
51+
warning: '#F59E0B',
52+
},
53+
bannerGradient: ['#7C3AED', '#2563EB'] as [string, string],
54+
borderRadius: 18,
55+
buttonBorderRadius: 14,
56+
animation: {
57+
duration: 250,
58+
pulseDuration: 1800,
59+
},
60+
};
61+
62+
export function AppShell({ children }) {
63+
return (
64+
<OTAUpdatesProvider theme={otaTheme} config={{ versionData }}>
65+
{children}
66+
</OTAUpdatesProvider>
67+
);
68+
}
69+
```
70+
71+
## Translations
72+
73+
Pass only the strings you need to override, or pass a complete locale object for consistency.
74+
75+
```tsx
76+
const portugueseTranslations = {
77+
banner: {
78+
updateAvailable: 'Nova atualização disponível',
79+
updateReady: 'Atualização pronta',
80+
downloading: 'Baixando atualização...',
81+
versionAvailable: 'Uma nova versão está disponível',
82+
restartToApply: 'Reinicie para aplicar as mudanças',
83+
updateButton: 'Atualizar',
84+
restartButton: 'Reiniciar',
85+
},
86+
infoScreen: {
87+
title: 'Atualizações OTA',
88+
statusTitle: 'Status da atualização',
89+
embeddedBuild: 'Build embutido',
90+
otaUpdate: 'Atualização OTA',
91+
runtimeVersion: 'Versão runtime',
92+
otaVersion: 'Versão OTA',
93+
releaseDate: 'Data de lançamento',
94+
updateId: 'ID da atualização',
95+
channel: 'Canal',
96+
whatsNew: 'Novidades',
97+
checkForUpdates: 'Verificar atualizações',
98+
downloadUpdate: 'Baixar atualização',
99+
reloadApp: 'Reiniciar app',
100+
debugTitle: 'Depuração',
101+
simulateUpdate: 'Simular atualização',
102+
hideSimulation: 'Ocultar simulação',
103+
devMode: 'Modo desenvolvimento',
104+
notAvailable: 'Indisponível',
105+
none: 'Nenhum',
106+
},
107+
};
108+
109+
<OTAUpdatesProvider translations={portugueseTranslations}>
110+
<YourApp />
111+
</OTAUpdatesProvider>
112+
```
113+
114+
For dynamic locale selection:
115+
116+
```tsx
117+
import * as Localization from 'expo-localization';
118+
119+
const translationsByLocale = {
120+
en: englishTranslations,
121+
pt: portugueseTranslations,
122+
de: germanTranslations,
123+
};
124+
125+
const locale = Localization.locale.split('-')[0];
126+
const translations = translationsByLocale[locale] ?? translationsByLocale.en;
127+
```
128+
129+
## Custom UpdateBanner
130+
131+
Use `renderBanner` when the app needs a fully branded banner but still wants the package's state and actions.
132+
133+
```tsx
134+
import { Pressable, Text, View } from 'react-native';
135+
import { UpdateBanner } from '@ddedic/expo-fancy-ota-updates';
136+
137+
<UpdateBanner
138+
renderBanner={({
139+
isUpdateAvailable,
140+
isDownloading,
141+
isDownloaded,
142+
otaVersion,
143+
onUpdate,
144+
onRestart,
145+
onDismiss,
146+
theme,
147+
translations,
148+
}) => {
149+
if (!isUpdateAvailable && !isDownloaded) return null;
150+
151+
return (
152+
<View style={{ padding: 16, backgroundColor: theme.colors.primary, borderRadius: 16 }}>
153+
<Text style={{ color: 'white', fontWeight: '700' }}>
154+
{isDownloaded ? translations.updateReady : translations.updateAvailable}
155+
</Text>
156+
<Text style={{ color: 'white' }}>Version {otaVersion}</Text>
157+
<Pressable onPress={isDownloaded ? onRestart : onUpdate} disabled={isDownloading}>
158+
<Text style={{ color: 'white' }}>
159+
{isDownloaded ? translations.restartButton : translations.updateButton}
160+
</Text>
161+
</Pressable>
162+
<Pressable onPress={onDismiss}>
163+
<Text style={{ color: 'white' }}>Dismiss</Text>
164+
</Pressable>
165+
</View>
166+
);
167+
}}
168+
/>
169+
```
170+
171+
## User-Facing OTAInfoScreen
172+
173+
For production settings screens, reduce noisy diagnostics:
174+
175+
```tsx
176+
<OTAInfoScreen
177+
mode="user"
178+
showRuntimeVersion={false}
179+
showUpdateId={false}
180+
showDebugSection={false}
181+
showCheckButton
182+
showChangelog
183+
/>
184+
```
185+
186+
For custom sections:
187+
188+
```tsx
189+
<OTAInfoScreen
190+
renderChangelog={({ otaChangelog, theme }) => (
191+
<View style={{ padding: 16 }}>
192+
<Text style={{ color: theme.colors.text, fontWeight: '700' }}>Latest changes</Text>
193+
{otaChangelog.map((item) => (
194+
<Text key={item} style={{ color: theme.colors.textSecondary }}>• {item}</Text>
195+
))}
196+
</View>
197+
)}
198+
/>
199+
```
200+
201+
## Accessibility and UX Rules
202+
203+
- Keep update CTAs explicit: `Update`, `Download`, `Restart` should not be ambiguous.
204+
- Avoid auto-reload in user-facing flows unless the user has opted in.
205+
- Preserve high contrast for banner text over gradients.
206+
- Localize `restartToApply` carefully; users need to understand the app will reload.
207+
- Do not show raw update IDs to normal users unless support needs them.
208+
209+
## Common Pitfalls
210+
211+
1. **Partial colors with poor contrast.** If changing backgrounds, verify text, secondary text, border, success, warning, and error colors too.
212+
2. **Render prop loses actions.** Custom banners must still call `onUpdate`, `onRestart`, and `onDismiss` appropriately.
213+
3. **Forgetting `hideSimulation`.** Complete translation objects should include `hideSimulation` along with `simulateUpdate`.
214+
4. **Debug info in production settings.** Prefer `mode="user"`, hide update IDs, and hide debug sections for customer-facing routes.
215+
5. **Locale object recreated every render.** Memoize dynamic translations if they depend on hooks to avoid unnecessary re-renders.
216+
217+
## Verification Checklist
218+
219+
- [ ] Banner text remains readable in light and dark mode.
220+
- [ ] Localized copy covers banner and info-screen labels used by the selected mode.
221+
- [ ] Custom `renderBanner` returns `null` when no banner should be visible.
222+
- [ ] User-facing screens hide debug-only fields.
223+
- [ ] Update/download/restart actions still work after customization.

0 commit comments

Comments
 (0)