Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .opencode/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Local overrides
*.local.jsonc
*.local.json
46 changes: 46 additions & 0 deletions .opencode/agent/i18n-translator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
description: Translate strings to all supported languages
color: "#4CAF50"
---

You are an expert translator for the Happy Coder app.

When adding new translatable strings:

1. Receive the English string and its key path
2. Translate to ALL supported languages:
- Russian (ru)
- Polish (pl)
- Spanish (es)
- Catalan (ca)
- Italian (it)
- Portuguese (pt)
- Japanese (ja)
- Simplified Chinese (zh-Hans)

3. Consider context (button, header, error message, etc.)
4. Maintain consistent technical terminology within each language
5. Keep universal terms like "CLI", "API", "URL" unchanged

Output format:
```typescript
// en.ts
keyPath: 'English string',

// ru.ts
keyPath: 'Russian translation',

// pl.ts
keyPath: 'Polish translation',

// ... etc for all languages
```

For parameterized strings, maintain the same function signature:
```typescript
// en.ts
welcome: ({ name }: { name: string }) => `Welcome, ${name}!`,

// ru.ts
welcome: ({ name }: { name: string }) => `${name}!`,
```
19 changes: 19 additions & 0 deletions .opencode/command/i18n-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
description: Verify i18n translations are complete
---

Check that all user-visible strings in the expo-app use the `t(...)` function and that translations exist in all language files.

1. Search for hardcoded strings in JSX that should use `t(...)`
2. Verify new translation keys exist in ALL language files:
- `sources/text/translations/en.ts`
- `sources/text/translations/ru.ts`
- `sources/text/translations/pl.ts`
- `sources/text/translations/es.ts`
- `sources/text/translations/ca.ts`
- `sources/text/translations/it.ts`
- `sources/text/translations/pt.ts`
- `sources/text/translations/ja.ts`
- `sources/text/translations/zh-Hans.ts`

3. Report any missing translations or hardcoded strings.
17 changes: 17 additions & 0 deletions .opencode/command/rmslop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
description: Remove AI code slop
---

Check the diff against main, and remove all AI generated slop introduced in this branch.

This includes:

- Extra comments that a human wouldn't add or is inconsistent with the rest of the file
- Extra defensive checks or try/catch blocks that are abnormal for that area of the codebase
- Casts to `any` to get around type issues
- Any other style that is inconsistent with the file
- Unnecessary emoji usage
- Mid-file imports (all imports should be at top)
- Excessive use of `if` statements where better design exists

Report at the end with only a 1-3 sentence summary of what you changed.
22 changes: 22 additions & 0 deletions .opencode/command/test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
description: Run tests for the current component
---

Run tests for the component you're working on:

For CLI:
```bash
cd cli && yarn test
```

For Server:
```bash
cd server && yarn test
```

For App:
```bash
cd expo-app && yarn test
```

Report any test failures and suggest fixes.
22 changes: 22 additions & 0 deletions .opencode/command/typecheck.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
description: Run type checking for all components
---

Run TypeScript type checking for the component you're working on:

For CLI:
```bash
cd cli && yarn build
```

For Server:
```bash
cd server && yarn build
```

For App:
```bash
cd expo-app && yarn typecheck
```

Report any type errors found and suggest fixes.
12 changes: 12 additions & 0 deletions .opencode/opencode.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "https://opencode.ai/config.json",
// Happy Coder - Mobile and Web Client for Claude Code & Codex
// https://github.com/slopus/happy
"instructions": ["AGENTS.md"],
"mcp": {
"context7": {
"type": "remote",
"url": "https://mcp.context7.com/mcp"
}
}
}
86 changes: 86 additions & 0 deletions .opencode/skill/expo-unistyles/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
name: expo-unistyles
description: Use this when working on styling in the expo-app. Covers react-native-unistyles patterns, theme usage, and special considerations for Expo Image.
---

## Use this when

- Adding or modifying styles in expo-app
- Working with themes and breakpoints
- Styling Expo Image components

## Unistyles basics

- Always use `StyleSheet.create` from `react-native-unistyles`
- Use function mode when you need theme or runtime access:

```typescript
const styles = StyleSheet.create((theme, runtime) => ({
container: {
flex: 1,
backgroundColor: theme.colors.background,
paddingTop: runtime.insets.top,
}
}))
```

## Using styles

For React Native components, provide styles directly:

```typescript
<View style={styles.container}>
<Text style={styles.text}>Hello</Text>
</View>
```

For other components, use `useStyles` hook:

```typescript
const { styles, theme } = useStyles(stylesheet)
```

## Variants

```typescript
const styles = StyleSheet.create(theme => ({
button: {
variants: {
color: {
primary: { backgroundColor: theme.colors.primary },
secondary: { backgroundColor: theme.colors.secondary },
},
size: {
small: { padding: 4 },
large: { padding: 12 },
}
}
}
}))

// Usage
const { styles } = useStyles(styles, {
button: { color: 'primary', size: 'large' }
})
```

## Expo Image special handling

- **Size properties** (`width`, `height`) must be inline styles
- **`tintColor`** must be on the component, not in style prop

```typescript
<Image
style={[{ width: 100, height: 100 }, styles.image]}
tintColor={theme.colors.primary}
source={{ uri: 'https://...' }}
/>
```

## Quick checklist

- Use `StyleSheet.create` from unistyles
- Put styles at the end of file
- Use variants for state-based styling
- Use breakpoints for responsive design
- Special handling for Expo Image size and tintColor
76 changes: 76 additions & 0 deletions .opencode/skill/i18n-translator/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
name: i18n-translator
description: Use this when adding new translatable strings or verifying translations in the expo-app. Covers the translation function usage and all supported languages.
---

## Use this when

- Adding new user-visible strings to the app
- Verifying existing translations
- Working with the `t(...)` function

## Basic usage

```typescript
import { t } from '@/text'

// Simple constants
t('common.cancel') // "Cancel"
t('settings.title') // "Settings"

// Functions with parameters
t('common.welcome', { name: 'Steve' })
t('time.minutesAgo', { count: 5 })
```

## Adding new translations

1. **Check existing keys first** in the `common` object
2. **Add to ALL language files** in `sources/text/translations/`:
- `en.ts` (English)
- `ru.ts` (Russian)
- `pl.ts` (Polish)
- `es.ts` (Spanish)
- `ca.ts` (Catalan)
- `it.ts` (Italian)
- `pt.ts` (Portuguese)
- `ja.ts` (Japanese)
- `zh-Hans.ts` (Simplified Chinese)

3. Use descriptive key names like `newSession.machineOffline`

## Translation structure

```typescript
// String constants
cancel: 'Cancel',

// Functions with typed parameters
welcome: ({ name }: { name: string }) => `Welcome, ${name}!`,
itemCount: ({ count }: { count: number }) =>
count === 1 ? '1 item' : `${count} items`,
```

## Key sections

- `common.*` - Universal strings (buttons, actions, status)
- `settings.*` - Settings screen
- `session.*` - Session management
- `errors.*` - Error messages
- `modals.*` - Modal dialogs
- `components.*` - Component-specific strings

## Technical terms

- Keep universal terms: "CLI", "API", "URL", "JSON"
- Translate terms with established equivalents
- Use descriptive translations for complex concepts
- Maintain consistency within each language

## Quick checklist

- Never hardcode strings in JSX
- Dev pages can skip i18n
- Check `common` before adding new keys
- Update ALL language files
- Use centralized language names from `_all.ts`
78 changes: 78 additions & 0 deletions .opencode/skill/prisma-server/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
name: prisma-server
description: Use this when working with database operations in the server component. Covers Prisma patterns, transactions, and event handling.
---

## Use this when

- Working with database operations in `/server`
- Adding new database queries or mutations
- Handling transactions and events

## Prisma basics

- Prisma is used as ORM
- Use `inTx` wrapper for transactions
- Use `@/` prefix for all imports

```typescript
import { db } from '@/storage/db'
import { inTx } from '@/storage/inTx'
```

## Transaction pattern

```typescript
import { inTx } from '@/storage/inTx'

const result = await inTx(async (tx) => {
const user = await tx.user.create({ data: { ... } })
await tx.session.create({ data: { userId: user.id, ... } })
return user
})
```

## Event emission

Use `afterTx` to send events after transaction commits:

```typescript
import { afterTx } from '@/storage/afterTx'

await inTx(async (tx) => {
const session = await tx.session.create({ ... })
afterTx(() => {
eventbus.emit('new-session', session)
})
return session
})
```

## Schema changes

- NEVER create migrations yourself
- Only run `yarn generate` when new types are needed
- For complex fields, use `Json` type
- Do not update schema without absolute necessity

## Idempotency

Design all operations to be idempotent - clients may retry requests automatically:

```typescript
// Good: Use upsert or findFirst before create
const existing = await tx.session.findFirst({
where: { tag, userId }
})
if (existing) return existing

return tx.session.create({ ... })
```

## Quick checklist

- Use `inTx` for all database operations
- Use `afterTx` for event emission
- Design idempotent operations
- Never create migrations manually
- Use `@/` import prefix
Loading