Skip to content
Merged
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
201 changes: 201 additions & 0 deletions .claude/skills/code-organization/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
---
name: code-organization
description: Organize Swift code with MARK comments and consistent file structure. Use when refactoring, creating new files, or improving code navigability.
---

# BetterFit Code Organization Skill

Use this skill when organizing Swift files, adding MARK comments, or improving code navigability.

## When to activate this skill

- Creating a new SwiftUI view or screen
- Refactoring existing code for better organization
- File has grown large and needs section markers
- User asks about code style or file structure
- Code review feedback mentions organization
- Want to improve Xcode minimap/navigability

## MARK comment conventions

Use `// MARK: - ...` to group related code into navigable sections.

### Rules

- Use `// MARK:` **above declarations** (computed properties, functions, nested types)
- **Don't** put MARKs inside view builder closures
- Keep marks short and consistent
- Add them when a file has multiple logical blocks

### SwiftUI view ordering

```swift
struct WorkoutHomeView: View {

// MARK: - State
@State private var workouts: [Workout] = []
@State private var searchText = ""
@StateObject private var viewModel = WorkoutViewModel()

// MARK: - Dependencies
let betterFit: BetterFit
let theme: AppTheme

// MARK: - Init
init(betterFit: BetterFit, theme: AppTheme) {
self.betterFit = betterFit
self.theme = theme
}

// MARK: - View
var body: some View {
ScrollView {
welcomeSection
suggestedWorkoutsSection
recentActivitySection
}
.navigationTitle("Workouts")
}

// MARK: - Welcome Section
private var welcomeSection: some View {
VStack(alignment: .leading) {
Text("Good morning,")
.font(theme.subheadline)
Text("Ready to train?")
.font(theme.headline)
}
}

// MARK: - Suggested Workouts Section
private var suggestedWorkoutsSection: some View {
VStack(alignment: .leading) {
Text("Suggested for You")
.font(theme.sectionHeader)

ForEach(viewModel.suggestedWorkouts) { workout in
WorkoutCard(workout: workout, theme: theme)
}
}
}

// MARK: - Recent Activity Section
private var recentActivitySection: some View {
VStack(alignment: .leading) {
Text("Recent")
.font(theme.sectionHeader)

ForEach(viewModel.recentWorkouts.prefix(3)) { workout in
WorkoutRow(workout: workout, theme: theme)
}
}
}

// MARK: - Data
private var filteredWorkouts: [Workout] {
guard !searchText.isEmpty else { return workouts }
return workouts.filter { $0.name.localizedCaseInsensitiveContains(searchText) }
}

// MARK: - Actions
private func startWorkout(_ workout: Workout) {
betterFit.startWorkout(workout)
}

// MARK: - Supporting Types
private struct WorkoutCard: View {
let workout: Workout
let theme: AppTheme

var body: some View {
// ...
}
}
}
```

### Manager/Service ordering

```swift
final class WorkoutManager: ObservableObject {

// MARK: - Published State
@Published var activeWorkout: Workout?
@Published var history: [Workout] = []

// MARK: - Dependencies
private let apiClient: APIClient
private let cache: Cache

// MARK: - Init
init(apiClient: APIClient, cache: Cache) {
self.apiClient = apiClient
self.cache = cache
}

// MARK: - Public API
func startWorkout(_ workout: Workout) { ... }
func completeWorkout(_ workout: Workout) { ... }
func deleteWorkout(_ workout: Workout) { ... }

// MARK: - Private Helpers
private func saveToCache(_ workout: Workout) { ... }
private func syncWithServer(_ workout: Workout) { ... }

// MARK: - Computed Properties
var totalWorkoutsCompleted: Int { history.count }
var currentStreak: Int { calculateStreak() }
}
```

## Section naming conventions

| Section | Use for | Example |
|---------|---------|---------|
| `// MARK: - State` | `@State`, `@StateObject`, `@ObservedObject` | `@State private var isLoading` |
| `// MARK: - Dependencies` | Injected services, managers | `let apiClient: APIClient` |
| `// MARK: - Init` | Initializers | `init(...)` |
| `// MARK: - View` | `body` and main view structure | `var body: some View` |
| `// MARK: - [Name] Section` | Cohesive UI section | `welcomeSection`, `statsSection` |
| `// MARK: - Sections` | Multiple related sections | List sections, cards |
| `// MARK: - Data` | Computed properties, static data | `filteredWorkouts`, `sampleData` |
| `// MARK: - Actions` | User action handlers | `startWorkout`, `handleTap` |
| `// MARK: - Public API` | Exposed methods (managers) | `func save()`, `func load()` |
| `// MARK: - Private Helpers` | Internal implementation | `private func validate()` |
| `// MARK: - Supporting Types` | Nested structs/enums | `private struct Row: View` |

## File organization principles

### Within a file (top to bottom)

1. **Imports**
2. **Type declaration**
3. **State/Properties** (published first, then private)
4. **Dependencies**
5. **Init**
6. **Main API/View** (`body`, key methods)
7. **Sections/Subviews** (computed properties)
8. **Data/Helpers** (computed properties, private methods)
9. **Actions** (event handlers)
10. **Supporting Types** (nested structs/enums)

### Across files

See `swift-file-splitting` skill for when/how to split large files.

## Xcode benefits

Proper MARK organization enables:
- **Minimap** shows clear section boundaries
- **Jump bar** (Ctrl+6) lists all sections
- **Code folding** works at section level
- **Quick navigation** in large files

## Quick checklist

- [ ] Add MARKs when file has 3+ logical sections
- [ ] Use `// MARK: - Name` (with dash for separator line)
- [ ] Place MARKs above declarations, not inside closures
- [ ] Follow consistent ordering: State → Dependencies → Init → View → Sections → Data → Actions → Types
- [ ] Keep section names short and consistent
- [ ] Don't over-MARK: simple files (<100 lines) may not need any
139 changes: 139 additions & 0 deletions .claude/skills/mise-workflows/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
---
name: mise-workflows
description: Fast development workflows for the BetterFit iOS project using mise. Use when building, testing, opening Xcode, or running lint.
---

# BetterFit Mise Workflows Skill

Use this skill when performing any build, test, lint, or Xcode operations on the BetterFit project.

## When to activate this skill

- User wants to build the project
- User wants to run tests
- User wants to open Xcode
- User wants to lint code
- User wants to build for iOS or watchOS
- User wants to create or render app showcase videos
- User asks about project commands or workflows
- User encounters build errors and needs to rebuild

## Core Commands

### Essential workflow (run these in order)

```bash
# 1. Install tools (first time or after tool updates)
mise install

# 2. Lint (fast feedback before building)
mise run lint

# 3. Build SwiftPM package
mise run build # Runs swift build

# 4. Run unit + integration tests
mise run test # Runs swift test
```

### iOS host app commands

```bash
# Open Xcode project (generates + opens)
mise run ios:open

# Build iOS for development
mise run ios:build:dev

# Build iOS for production
mise run ios:build:prod

# Generate Xcode project only
mise run ios:gen

# Boot iOS 26 simulator
mise run ios:sim:boot26

# Run UI tests
mise run ios:test:ui
```

### watchOS app commands

```bash
# Open watchOS Xcode project
mise run watch:open

# Build watchOS
mise run watch:build
```

### Video showcase commands (Remotion + Bun)

```bash
# Install video dependencies (uses Bun)
mise run video:install

# Preview in Remotion Studio
mise run video:dev

# Render the main showcase video
mise run video:render:showcase

# Render any composition
mise run video:render -- <CompositionName>

# Render a single frame for QA
mise run video:still -- <CompositionName> <out.png> --frame=N
```

**Important:** Video tasks run in the `videos/` directory using Bun (not npm). See `remotion-app-showcase` skill for video creation guidance.

### Important rules

- **Never run `xcodegen generate` directly** — always use `mise run ios:gen` or `mise run ios:open` to ensure Supabase credentials are injected from `.env`
- After making changes, run `mise run lint` first (quicker feedback than building)
- Use `mise run ios:build:dev` for quick iteration; `mise run ios:build:prod` for release testing

## Workflow patterns

### Development loop
```
Make changes → mise run lint → mise run build → mise run test
```

### UI iteration loop
```
Make changes → mise run ios:build:dev → test in simulator
```

### Pre-commit checklist
```
mise run lint
mise run test
```

### When build fails
1. Run `mise run lint` to catch syntax/style issues first
2. Check if Supabase credentials are configured (see supabase-setup skill)
3. Try clean build: `mise run ios:build:dev` (rebuilds from scratch)
4. For SwiftPM issues: `swift package resolve` then retry

## Project structure context

- **SwiftPM library**: `Sources/BetterFit/` — core business logic
- **iOS host app**: `Apps/iOS/BetterFitApp/` — demo UI, depends on SwiftPM package
- **watchOS app**: `Apps/iOS/BetterFitWatchApp/` — watch app
- **XcodeGen config**: `Apps/iOS/project.yml` — don't hand-edit `.xcodeproj`

## Common issues

### "No such module 'BetterFit'"
- Run `mise run ios:gen` to regenerate Xcode project with proper SPM linking

### Xcode project out of sync
- Don't edit `.xcodeproj` directly
- Run `mise run ios:open` to regenerate from `project.yml`

### Build fails after git clean
- `.env` may be deleted — check supabase-setup skill for reconfiguration
Loading
Loading