A macOS utility to track home-manager JJ repo status
1# Agent Guidelines for HomeManagerStatus
2
3This document provides guidelines for AI coding agents working on this codebase.
4
5## Project Overview
6
7HomeManagerStatus is a macOS menu bar app written in Swift that displays the Jujutsu (jj) status of `~/.config/home-manager`. It's packaged with Nix and includes a home-manager module for launchd integration.
8
9## Build Commands
10
11### Swift (development)
12
13```bash
14# Build (debug)
15swift build
16
17# Build (release)
18swift build -c release
19
20# Run the app directly (for testing)
21swift run
22```
23
24### Nix (production)
25
26```bash
27# Build the package
28nix build
29
30# Enter development shell (provides swift-format)
31nix develop
32
33# Format all files (Nix, Shell, Swift)
34nix fmt
35
36# Check flake validity
37nix flake check
38```
39
40## Test Commands
41
42```bash
43# Run all tests
44swift test
45
46# Run a single test file
47swift test --filter HomeManagerStatusTests.StatusParserTests
48
49# Run a single test method
50swift test --filter HomeManagerStatusTests.StatusParserTests/testParseCommitCountEmpty
51
52# Run tests with verbose output
53swift test --verbose
54```
55
56## Code Style
57
58### Formatting
59
60This project uses `swift-format` for Swift code formatting, configured via treefmt-nix. Always run `nix fmt` before committing.
61
62Key formatting rules:
63- 2-space indentation
64- Line length: 100 characters (soft limit)
65- Trailing commas in multi-line collections
66
67### Imports
68
69Order imports alphabetically. Standard library and system frameworks first:
70
71```swift
72import Foundation
73import SwiftUI
74import XCTest
75
76@testable import HomeManagerStatus
77```
78
79### Naming Conventions
80
81| Type | Convention | Example |
82|------|------------|---------|
83| Types (struct, class, enum, protocol) | PascalCase | `RepoStatus`, `StatusParser` |
84| Functions and methods | camelCase | `parseCommitCount`, `startPolling` |
85| Variables and properties | camelCase | `refreshInterval`, `repoPath` |
86| Constants | camelCase | `fixedDate` |
87| Enum cases | camelCase | `unexpectedDirtyOutput` |
88
89### Types and Protocols
90
91- Prefer `struct` over `class` unless reference semantics are required
92- Use `@MainActor` for UI-bound types (e.g., `StatusChecker`)
93- Define protocols for testability (e.g., `JujutsuCommandRunner`)
94- Use `enum` with no cases for namespacing pure functions (e.g., `StatusParser`)
95
96### Error Handling
97
98- Define errors as nested enums conforming to `LocalizedError`
99- Provide `errorDescription` for user-facing messages
100- Use `Result` for mock/test code, `throws` for production code
101
102```swift
103enum ParserError: LocalizedError {
104 case unexpectedDirtyOutput(String)
105
106 var errorDescription: String? {
107 switch self {
108 case .unexpectedDirtyOutput(let output):
109 return "Unexpected dirty status output: '\(output)'"
110 }
111 }
112}
113```
114
115### Documentation
116
117- Use `///` doc comments for public APIs
118- Include parameter and return descriptions for non-trivial functions
119- Use `// MARK: -` to organize code sections
120
121```swift
122/// Parse the output of `jj log` that lists commit short IDs (one per line)
123/// and return the count of commits.
124///
125/// - Parameter output: Raw stdout from jj log command
126/// - Returns: Number of commits listed
127static func parseCommitCount(from output: String) -> Int { ... }
128```
129
130### Testing
131
132- Test files mirror source files: `StatusParser.swift` -> `StatusParserTests.swift`
133- Use descriptive test names: `testParseCommitCountEmpty`, `testRefreshWithError`
134- Group related tests with `// MARK: -` comments
135- Use fixed dates for deterministic tests: `let fixedDate = Date(timeIntervalSince1970: 1_700_000_000)`
136- Create mock implementations of protocols for unit testing
137
138### Async/Await
139
140- Use `async/await` for asynchronous operations
141- Use `withCheckedThrowingContinuation` to bridge callback-based APIs
142- Mark test methods as `async` when testing async code
143- Use `@MainActor` for classes that publish to SwiftUI
144
145## Project Structure
146
147```
148.
149├── Sources/HomeManagerStatus/
150│ ├── HomeManagerStatusApp.swift # App entry point, SwiftUI views
151│ ├── StatusChecker.swift # Orchestrates polling and state
152│ ├── StatusParser.swift # Pure parsing functions
153│ ├── RepoStatus.swift # Data model
154│ └── JujutsuCommandRunner.swift # Protocol + live implementation
155├── Tests/HomeManagerStatusTests/
156│ ├── StatusCheckerTests.swift
157│ ├── StatusParserTests.swift
158│ └── RepoStatusTests.swift
159├── nix/
160│ ├── package.nix # Nix derivation
161│ └── modules/home-manager.nix # home-manager module
162├── Package.swift # Swift package manifest
163├── Info.plist # macOS app bundle metadata
164└── flake.nix # Nix flake
165```
166
167## Version Control
168
169This project uses Jujutsu (jj). Key commands:
170
171```bash
172jj status # Show working copy changes
173jj diff --git # Show diff in git format
174jj describe -m "msg" # Set commit message (don't use jj commit)
175jj new # Create new change
176```
177
178Do NOT use interactive jj commands (`jj commit`, `jj split`) without `-m` flag.
179
180## Nix Module
181
182The home-manager module exposes:
183- `programs.jj-home-manager-status.enable` (bool)
184- `programs.jj-home-manager-status.package` (package)
185
186When modifying the module, test with:
187```bash
188nix flake check
189nix eval '.#homeManagerModules.default'
190```
191
192## Platform Requirements
193
194- macOS 13.0+ (Ventura)
195- Swift 5.9+
196- Jujutsu (jj) in PATH