spank

GitHub

Slap your MacBook, it yells back. Uses Apple Silicon accelerometer via IOKit HID.

RAW Rules

AGENTS.md

# AGENTS.md

> Guidelines for AI agents working in this repository.

## Project Overview

**spank** is a macOS CLI tool that detects physical hits/slaps on Apple Silicon MacBooks via the accelerometer and plays audio responses. Single-file Go application with embedded MP3 assets.

- **Platform**: macOS on Apple Silicon (M2+) only
- **Runtime requirement**: `sudo` (for IOKit HID accelerometer access)
- **Architecture**: Single `main.go` file with embedded audio assets

## Commands

### Build & Run

```bash
# Build
go build -o spank .

# Run (requires sudo)
sudo ./spank
sudo ./spank --sexy      # escalating responses mode
sudo ./spank --halo      # Halo death sounds mode
sudo ./spank --custom /path/to/mp3s  # custom audio directory
```

### Install

```bash
go install github.com/taigrr/spank@latest
```

### Release

Releases are automated via GitHub Actions + GoReleaser Pro when a `v*` tag is pushed:

```bash
git tag v1.0.0
git push origin v1.0.0
```

## Code Organization

```
spank/
├── main.go              # All application code (single file)
├── audio/
│   ├── pain/            # Default "ow!" responses (10 MP3s)
│   ├── sexy/            # Escalating responses (60 MP3s)
│   └── halo/            # Halo death sounds (9 MP3s)
├── go.mod
├── .goreleaser.yaml     # Release configuration
└── .github/workflows/   # CI/CD
```

## Key Dependencies

| Package | Purpose |
|---------|---------|
| `github.com/taigrr/apple-silicon-accelerometer` | Reads accelerometer via IOKit HID |
| `github.com/gopxl/beep/v2` | Audio playback (MP3 decoding, speaker output) |
| `github.com/spf13/cobra` | CLI framework |
| `github.com/charmbracelet/fang` | CLI config/execution wrapper |

## Code Patterns

### Embedded Assets

Audio files are embedded at compile time using `//go:embed`:

```go
//go:embed audio/pain/*.mp3
var painAudio embed.FS
```

### Play Modes

Two playback strategies in `playMode`:
- `modeRandom`: Random file selection (pain, halo, custom modes)
- `modeEscalation`: Intensity increases with slap frequency (sexy mode)

### Slap Detection Flow

1. `sensor.Run()` reads accelerometer in background goroutine
2. Data shared via `shm.RingBuffer` (POSIX shared memory)
3. `detector.New()` processes samples with vibration detection algorithms
4. Events trigger audio playback with 750ms cooldown

### Concurrency

- `speakerMu sync.Mutex` protects speaker initialization
- `slapTracker.mu sync.Mutex` protects slap scoring state
- Audio playback runs in goroutines (`go playAudio(...)`)

## Constants

Key tuning parameters in `main.go`:

| Constant | Value | Purpose |
|----------|-------|---------|
| `decayHalfLife` | 30s | How fast escalation fades |
| `slapCooldown` | 750ms | Minimum time between audio plays |
| `sensorPollInterval` | 10ms | Accelerometer polling rate |
| `maxSampleBatch` | 200 | Max samples processed per tick |

## Gotchas

1. **Root required**: The app must run with `sudo` for IOKit HID access. The `run()` function checks `os.Geteuid() != 0`.

2. **Apple Silicon only**: Only builds for `darwin/arm64`. Intel Macs are not supported.

3. **Private dependency**: `github.com/taigrr/apple-silicon-accelerometer` requires `GOPRIVATE` setting and GitHub PAT for CI.

4. **Single file**: All code is in `main.go`. When adding features, follow the existing pattern of types and functions in the same file.

5. **Mutually exclusive modes**: `--sexy`, `--halo`, and `--custom` flags cannot be combined.

6. **CGO disabled**: Builds use `CGO_ENABLED=0` despite targeting macOS.

## Adding Audio

To add a new sound pack:

1. Create directory under `audio/`
2. Add MP3 files (numbered for escalation mode, any names for random)
3. Add `//go:embed audio/newpack/*.mp3` variable
4. Add flag and case in `run()` switch statement
5. Create `soundPack` with appropriate `playMode`

## Version

Version is injected via ldflags at build time:

```go
var version = "dev"
```

GoReleaser sets `-X main.version={{.Version}}` during release builds.