Skip to content

Commit a8260f4

Browse files
berkozeroclaude
andcommitted
Add tests, CI, and open-source infrastructure
- Unit tests for HIDKeyMap (lookup, namedKey, parseModifier) - GitHub Actions CI workflow (build + test on macOS 15) - Track Package.resolved for reproducible builds - Makefile with build/release/install/test/clean targets - CLAUDE.md with project conventions for AI agents - GitHub issue templates (bug report, feature request) - CODE_OF_CONDUCT.md (Contributor Covenant v2.1) - CONTRIBUTING.md with dev setup and code style guidelines Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7d0195a commit a8260f4

10 files changed

Lines changed: 404 additions & 1 deletion

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
name: Bug Report
3+
about: Report something that isn't working correctly
4+
title: ''
5+
labels: bug
6+
assignees: ''
7+
---
8+
9+
**What you ran**
10+
11+
```
12+
iphonebase <command> <flags>
13+
```
14+
15+
**What you expected**
16+
17+
A clear description of the expected behavior.
18+
19+
**What actually happened**
20+
21+
What went wrong. Include error messages or unexpected output.
22+
23+
**Environment**
24+
25+
- macOS version:
26+
- iPhone model:
27+
- Karabiner-Elements version:
28+
- iphonebase version (`iphonebase --version`):
29+
30+
**Additional context**
31+
32+
Anything else — screenshots, `--verbose` output, etc.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
name: Feature Request
3+
about: Suggest a new feature or improvement
4+
title: ''
5+
labels: enhancement
6+
assignees: ''
7+
---
8+
9+
**Use case**
10+
11+
Describe the problem or workflow this feature would help with.
12+
13+
**Proposed solution**
14+
15+
How you imagine this working (CLI interface, flags, output format).
16+
17+
**Alternatives considered**
18+
19+
Any workarounds you've tried or other approaches.
20+
21+
**Additional context**
22+
23+
Anything else — screenshots, links to related issues, etc.

.github/workflows/ci.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build-and-test:
11+
runs-on: macos-15
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Build
16+
run: swift build
17+
18+
- name: Run tests
19+
run: swift test

.gitignore

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
1+
# macOS
12
.DS_Store
3+
4+
# Swift Package Manager
25
/.build
36
/Packages
7+
8+
# Xcode
49
xcuserdata/
510
DerivedData/
11+
*.xcodeproj
612
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
13+
14+
# Credentials
715
.netrc
8-
*.xcodeproj
16+
17+
# Claude Code
18+
.claude/
19+
20+
# Editor
21+
*.swp
22+
*~

CLAUDE.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# iphonebase
2+
3+
Swift CLI to control iPhone via macOS iPhone Mirroring. Built for AI agents (OpenClaw, Claude Code, MCP) and standalone terminal use.
4+
5+
## Tech Stack
6+
- Swift 5.9+, macOS 14+ (requires Sequoia 15.0+ at runtime)
7+
- ArgumentParser 1.3.0
8+
- Frameworks: ScreenCaptureKit, Vision, CoreGraphics, AppKit
9+
- Karabiner-Elements required (DriverKit virtual HID for input injection)
10+
11+
## Build & Run
12+
swift build # debug
13+
swift build -c release # release
14+
sudo cp .build/release/iphonebase /usr/local/bin/ # install
15+
16+
## Project Structure
17+
- Sources/IPhoneBaseCore/ — library: WindowManager, ScreenCapture, OCREngine, InputInjector, HIDKeyMap
18+
- Sources/iphonebase/ — CLI entry point + Commands/
19+
- skills/iphonebase/ — OpenClaw skill definition (SKILL.md)
20+
21+
## Adding a New Command
22+
1. Create Sources/iphonebase/Commands/XxxCommand.swift
23+
2. Implement AsyncParsableCommand (or ParsableCommand for sync-only)
24+
3. Add XxxCommand.self to subcommands array in IPhoneBase.swift
25+
4. Include --json flag for structured output
26+
27+
## Code Conventions
28+
- Import order: ArgumentParser, IPhoneBaseCore, Foundation
29+
- All commands support --json (use JSONSerialization with .prettyPrinted)
30+
- Results to stdout, debug/verbose to stderr
31+
- InputInjector pattern: connect() then defer { disconnect() }
32+
- Call wm.bringToFront() before any input injection
33+
- Errors: typed enums with CustomStringConvertible; throw ExitCode.failure for user errors
34+
35+
## Coordinate System (critical)
36+
- ScreenCapture captures at 2x retina resolution
37+
- OCR (Vision) returns normalized coords with bottom-left origin — must invert Y
38+
- tap --text handles conversion automatically; raw tap x y is relative to window (screen points)
39+
- All InputInjector operations use absolute screen coordinates (window.bounds.origin + offset)
40+
41+
## Input Injection Gotchas
42+
- iPhone Mirroring blocks CGEvent clicks — only Karabiner virtual HID works
43+
- Tap sequence: CGWarp cursor → nudge-sync virtual pointer (3x 1px/-1px) → click via HID
44+
- Timing delays (usleep) throughout InputInjector are tuned values, not arbitrary
45+
- Karabiner daemon must be running (not just installed)
46+
47+
## OpenClaw Skill
48+
- Skill at skills/iphonebase/SKILL.md follows AgentSkills spec (YAML frontmatter + markdown)
49+
- Requires bins: ["iphonebase"], os: ["darwin"]
50+
- Install to ~/.openclaw/skills/ for agent discovery
51+
52+
## Commits
53+
- Imperative mood ("Add feature" not "Added feature")
54+
- First line under 72 characters

CODE_OF_CONDUCT.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Code of Conduct
2+
3+
This project follows the [Contributor Covenant v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
4+
5+
By participating in this project, you agree to abide by its terms.
6+
7+
## Reporting
8+
9+
If you experience or witness unacceptable behavior, please open an issue at
10+
https://github.com/berkozero/iphonebase/issues or contact the maintainers directly.
11+
12+
All reports will be reviewed and handled with discretion.

CONTRIBUTING.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Contributing to iphonebase
2+
3+
Thanks for your interest in contributing! Here's how to get started.
4+
5+
## Reporting Bugs
6+
7+
Open a [GitHub issue](https://github.com/berkozero/iphonebase/issues/new?template=bug_report.md) with:
8+
9+
- What you ran (command + flags)
10+
- What you expected
11+
- What actually happened
12+
- macOS version, iPhone model, and Karabiner-Elements version
13+
14+
If a command fails silently, re-run with `--verbose` (where supported) and include the stderr output.
15+
16+
## Suggesting Features
17+
18+
Open a [feature request](https://github.com/berkozero/iphonebase/issues/new?template=feature_request.md). Describe the use case, not just the solution.
19+
20+
## Development Setup
21+
22+
```bash
23+
git clone https://github.com/berkozero/iphonebase.git
24+
cd iphonebase
25+
swift build
26+
```
27+
28+
Requirements:
29+
- macOS 15.0+ (Sequoia)
30+
- Xcode 16+ or Swift 6.0+ toolchain
31+
- [Karabiner-Elements](https://karabiner-elements.pqrs.org/) installed
32+
- iPhone Mirroring active (for testing)
33+
34+
## Submitting Changes
35+
36+
1. Fork the repo and create a branch from `main`
37+
2. Make your changes
38+
3. Test against a real iPhone Mirroring session
39+
4. Run `swift build` to verify it compiles
40+
5. Open a pull request with a clear description of what and why
41+
42+
### Code Style
43+
44+
- Follow existing conventions in the codebase
45+
- Keep functions focused and small
46+
- Use `--json` output for any new commands
47+
- Write to stderr for debug/verbose output, stdout for results
48+
49+
### Commit Messages
50+
51+
- Use imperative mood ("Add feature" not "Added feature")
52+
- First line under 72 characters
53+
- Explain *why*, not just *what*, in the body if needed
54+
55+
## Code of Conduct
56+
57+
This project follows the [Contributor Covenant](CODE_OF_CONDUCT.md). Be kind and constructive.

Makefile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.PHONY: build release install test clean
2+
3+
build:
4+
swift build
5+
6+
release:
7+
swift build -c release
8+
9+
install: release
10+
cp .build/release/iphonebase /usr/local/bin/
11+
12+
test:
13+
swift test
14+
15+
clean:
16+
swift package clean

Package.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,9 @@ let package = Package(
2828
.product(name: "ArgumentParser", package: "swift-argument-parser"),
2929
]
3030
),
31+
.testTarget(
32+
name: "IPhoneBaseCoreTests",
33+
dependencies: ["IPhoneBaseCore"]
34+
),
3135
]
3236
)

0 commit comments

Comments
 (0)