Skip to content

Commit bf28acd

Browse files
authored
docs: add AGENTS.md for agentic coding guidelines (#142)
1 parent 14ef306 commit bf28acd

File tree

1 file changed

+201
-0
lines changed

1 file changed

+201
-0
lines changed

AGENTS.md

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# MiniSim - Agent Guidelines
2+
3+
MiniSim is a macOS menu bar utility for launching iOS simulators and Android emulators. Written in Swift and AppKit.
4+
5+
## Build Commands
6+
7+
```bash
8+
# Build the project
9+
xcodebuild -scheme MiniSim -configuration Debug build
10+
11+
# Build for release
12+
xcodebuild -scheme MiniSim -configuration Release build
13+
14+
# Clean build
15+
xcodebuild -scheme MiniSim clean build
16+
```
17+
18+
## Testing
19+
20+
```bash
21+
# Run all tests
22+
xcodebuild test -scheme MiniSim -destination 'platform=macOS'
23+
24+
# Run a single test file
25+
xcodebuild test -scheme MiniSim -destination 'platform=macOS' \
26+
-only-testing:MiniSimTests/DeviceParserTests
27+
28+
# Run a single test method
29+
xcodebuild test -scheme MiniSim -destination 'platform=macOS' \
30+
-only-testing:MiniSimTests/DeviceParserTests/testIOSSimulatorParser
31+
```
32+
33+
## Linting
34+
35+
SwiftLint is integrated via SPM. Config in `.swiftlint.yml`.
36+
37+
```bash
38+
# Run SwiftLint
39+
swiftlint
40+
41+
# Auto-fix issues
42+
swiftlint --fix
43+
```
44+
45+
## Project Structure
46+
47+
```
48+
MiniSim/
49+
├── Model/ # Data models (Device, Command, Platform, etc.)
50+
├── Service/ # Business logic and services
51+
│ ├── CustomErrors/ # Custom error types
52+
│ └── Terminal/ # Terminal integration
53+
├── Views/ # SwiftUI views
54+
│ ├── Onboarding/ # Onboarding flow
55+
│ ├── CustomCommands/ # Custom commands UI
56+
│ └── ParametersTable/ # Parameters management
57+
├── Extensions/ # Swift extensions
58+
├── MenuItems/ # NSMenu item implementations
59+
├── Components/ # Reusable UI components
60+
└── AppleScript Commands/ # AppleScript integration
61+
```
62+
63+
## Code Style Guidelines
64+
65+
### Imports
66+
- Sort imports alphabetically (enforced by SwiftLint `sorted_imports`)
67+
- Group Foundation/AppKit imports first, then third-party
68+
69+
```swift
70+
import AppKit
71+
import Foundation
72+
import UserNotifications
73+
```
74+
75+
### Naming Conventions
76+
- Types: `PascalCase` (e.g., `DeviceService`, `ActionFactory`)
77+
- Variables/functions: `camelCase`
78+
- Enums: `PascalCase` for type, `camelCase` for cases
79+
- Protocols: Suffix with `Protocol` for dependency injection (e.g., `ShellProtocol`, `ADBProtocol`)
80+
- `id` and `ID` are allowed as identifier names
81+
82+
### Types and Protocols
83+
- Use protocols for dependency injection and testability
84+
- Prefer `struct` for data models, `class` for services with state
85+
- Use `final class` when inheritance is not needed
86+
87+
```swift
88+
protocol DeviceServiceCommon {
89+
var shell: ShellProtocol { get set }
90+
var device: Device { get }
91+
func deleteDevice() throws
92+
}
93+
94+
struct Device: Hashable, Codable {
95+
var name: String
96+
var identifier: String?
97+
var booted: Bool
98+
}
99+
```
100+
101+
### Error Handling
102+
- Define custom errors as enums conforming to `Error` and `LocalizedError`
103+
- Group related errors in `Service/CustomErrors/`
104+
- Provide meaningful `errorDescription` for user-facing messages
105+
106+
```swift
107+
enum DeviceError: Error, Equatable {
108+
case deviceNotFound
109+
case xcodeError
110+
case unexpected(code: Int)
111+
}
112+
113+
extension DeviceError: LocalizedError {
114+
public var errorDescription: String? {
115+
switch self {
116+
case .deviceNotFound:
117+
return NSLocalizedString("Selected device was not found...", comment: "")
118+
}
119+
}
120+
}
121+
```
122+
123+
### SwiftUI Views
124+
- Use MVVM pattern with nested `ViewModel` class
125+
- ViewModels extend from view type (e.g., `CustomCommands.ViewModel`)
126+
- Use `@Published` for observable state
127+
128+
```swift
129+
extension CustomCommands {
130+
class ViewModel: ObservableObject {
131+
@Published var commands: [Command] = []
132+
@Published var showForm = false
133+
}
134+
}
135+
```
136+
137+
### Extensions
138+
- File naming: `TypeName+Feature.swift` (e.g., `UserDefaults+Configuration.swift`)
139+
- One extension purpose per file
140+
141+
### Factory Pattern
142+
- Use factory classes for creating platform-specific implementations
143+
- Follow `ActionFactory` pattern for iOS/Android differentiation
144+
145+
```swift
146+
class AndroidActionFactory: ActionFactory {
147+
static func createAction(for tag: SubMenuItems.Tags, ...) -> any Action {
148+
switch tag {
149+
case .copyName: return CopyNameAction(device: device)
150+
}
151+
}
152+
}
153+
```
154+
155+
### Testing
156+
- Test files mirror source structure in `MiniSimTests/`
157+
- Use `@testable import MiniSim`
158+
- Create stub/mock classes for dependencies (see `Mocks/ShellStub.swift`)
159+
- Override class methods in nested test classes for mocking
160+
161+
```swift
162+
class ADBTests: XCTestCase {
163+
var shellStub: ShellStub!
164+
165+
override func setUp() {
166+
shellStub = ShellStub()
167+
ADB.shell = shellStub
168+
}
169+
}
170+
```
171+
172+
### SwiftLint Rules (Key Enabled)
173+
- `sorted_imports` - alphabetical import ordering
174+
- `implicit_return` - omit return in single-expression closures
175+
- `trailing_closure` - use trailing closure syntax
176+
- `vertical_whitespace_closing_braces` - no blank lines before closing braces
177+
- `shorthand_optional_binding` - use `if let x` instead of `if let x = x`
178+
- `weak_delegate` - delegates should be weak
179+
180+
### Disabled Rules
181+
- `nesting` - nested types are allowed
182+
183+
### Documentation
184+
- Add JSDoc-style comments for new public functions
185+
- Focus on "why" not "what" for complex logic
186+
- No comments in JSX/SwiftUI describing what renders
187+
188+
### Threading
189+
- Use `Thread.assertBackgroundThread()` for background-only operations
190+
- Main thread assertions available via `Thread+Asserts.swift`
191+
192+
## Dependencies (SPM)
193+
- `KeyboardShortcuts` - Global keyboard shortcuts
194+
- `LaunchAtLogin` - Login item management
195+
- `Sparkle` - Auto-updates
196+
- `ShellOut` - Shell command execution
197+
- `CodeEditor` - Code editing in preferences
198+
- `SymbolPicker` - SF Symbol picker
199+
- `Settings` - Preferences window
200+
- `AcknowList` - Acknowledgements
201+
- `SwiftLint` - Linting (build plugin)

0 commit comments

Comments
 (0)