Skip to content
Closed
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
55 changes: 55 additions & 0 deletions apps/moss-pikachu/.cursor/skills/moss-pikachu/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
name: moss-pikachu
description: >-
Build and maintain Moss Pikachu, a macOS menu bar semantic file search app
using Moss Python SDK (PyPI moss>=1.6.0), FSEvents, SwiftUI, and Pikachu pet
animations. Use when working on MossPikachu, moss_worker.py, FileMonitor,
SearchService, menu bar overlay, or Moss integration in this repository.
---

# Moss Pikachu Agent Skill

## Read order

1. [architecture.md](architecture.md)
2. [moss-integration.md](moss-integration.md)
3. [macos-patterns.md](macos-patterns.md)
4. [ui-animations.md](ui-animations.md)
5. [pitfalls.md](pitfalls.md)

## Non-negotiables

- **Xcode `.app` bundle** — not a root-level SPM executable for the menu bar app
- **macOS-only target** — `SUPPORTED_PLATFORMS = macosx`
- **Python worker** for Moss on macOS — Moss Swift SPM is iOS-only
- **`pip install moss>=1.6.0`** — GitHub `main` `sdks/python/sdk` lacks `SessionIndex` API
- **No hardcoded credentials** — `MOSS_PROJECT_ID` / `MOSS_PROJECT_KEY` via Keychain or env
- **NSPanel** for search overlay — not `WindowGroup` (avoids Dock icon)

## Phase gates

Run before advancing phases:

```bash
./.cursor/skills/moss-pikachu/scripts/validate-phase.sh 1
./.cursor/skills/moss-pikachu/scripts/validate-phase.sh 2
./.cursor/skills/moss-pikachu/scripts/validate-phase.sh 3
```

## Task sequencing

| Phase | Scope |
|-------|-------|
| 1 | Menu bar, hotkey, search overlay shell, settings window |
| 2 | FileMonitor, moss_worker.py, MossBridge, SearchService |
| 3 | Pikachu animations, live search UI, settings, polish |

Do not wire live search before MossBridge + SearchService compile and pass Phase 2 validation.

## Code conventions

- `@MainActor` for all UI updates
- `async/await` for MossBridge calls (not callbacks)
- Line-delimited JSON between Swift and Python (`\n` terminated)
- Debounce FSEvents (100ms) and search input (200ms)
- `MARK:` sections in Swift service files
46 changes: 46 additions & 0 deletions apps/moss-pikachu/.cursor/skills/moss-pikachu/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Architecture

## Component map

```
AppDelegate
├── NSStatusBar (menu: Search, Settings, Quit)
├── HotKeyManager (⌘⇧M)
├── SearchOverlayController (NSPanel)
└── SettingsWindowController

SearchOverlayView
├── PikachuPetView
├── ResultsListView
└── SearchService (injected)

SearchService
├── FileMonitor (FSEvents)
├── IndexManager (file manifest in Application Support)
└── MossBridge → moss_worker.py subprocess
```

## Data flow

1. **Launch:** SearchService.initialize() → start MossBridge → init_session → scan folders → add_docs
2. **File change:** FileMonitor → debounce → SearchService.indexFiles → MossBridge.addDocs
3. **Search:** SearchOverlayView → debounced query → SearchService.search → MossBridge.query → results
4. **Quit:** FileMonitor.stop → MossBridge.saveSession (push_index if cloud sync) → terminate worker

## File ownership

| Path | Owner |
|------|-------|
| `MossPikachu/AppDelegate.swift` | Menu bar lifecycle |
| `MossPikachu/Views/SearchOverlayController.swift` | NSPanel window chrome |
| `MossPikachu/Services/MossBridge.swift` | Subprocess JSON protocol |
| `MossPikachu/Resources/moss_worker.py` | Moss SessionIndex loop |
| `~/Library/Application Support/MossPikachu/` | Index manifest, logs |

## Persistence (no Python disk API)

Python `SessionIndex` has no `save_to_disk` / `load_from_disk`. Persistence strategy:

- **In-session:** worker holds SessionIndex in memory while app runs
- **Across launches:** Swift `IndexManager` stores `{path, mtime}` manifest; rescan only changed files
- **Optional cloud:** `push_index()` when user enables Moss cloud sync in Settings
37 changes: 37 additions & 0 deletions apps/moss-pikachu/.cursor/skills/moss-pikachu/macos-patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# macOS Patterns

## Menu bar agent (no Dock icon)

`Info.plist`:
```xml
<key>LSUIElement</key>
<true/>
```

Or build setting: `INFOPLIST_KEY_LSUIElement = YES`

## Search overlay (NSPanel)

- Subclass `NSPanel`, style `.nonactivatingPanel` + `.fullSizeContentView`
- `level = .floating`, `isOpaque = false`, `backgroundColor = .clear`
- `collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]`
- `isMovableByWindowBackground = false`
- Click-outside: global `NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown)`

## Global hotkey (Carbon, no deps)

- Key code `46` = M (US keyboard)
- Modifiers: `cmdKey | shiftKey`
- `RegisterEventHotKey` + `InstallEventHandler` for `kEventHotKeyPressed`

## App Sandbox

Disabled for MVP (`ENABLE_APP_SANDBOX = NO`) — required for:
- FSEvents on ~/Documents, ~/Desktop, ~/Downloads
- Spawning Python subprocess with venv outside bundle

Re-enable with entitlements before App Store distribution.

## Credentials

Read from Keychain service `dev.moss.pikachu` or fall back to environment variables for dev.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Moss Integration

## Install

```bash
./scripts/setup-moss-venv.sh
export MOSS_PROJECT_ID=... MOSS_PROJECT_KEY=...
```

Uses **PyPI `moss>=1.6.0`**, not editable install from `vendor/moss/sdks/python/sdk` (main branch lacks sessions).

`vendor/moss` submodule is for examples/reference only.

## Worker JSON protocol (line-delimited)

### Request → Response

| action | input | output |
|--------|-------|--------|
| `ping` | `{}` | `{"status":"ok"}` |
| `init_session` | `{"index_name":"documents"}` | `{"status":"ok","doc_count":N}` |
| `add_docs` | `{"files":["/path/a.md"]}` | `{"status":"ok","added":N,"updated":M}` |
| `query` | `{"query":"text","top_k":5}` | `{"results":[...],"timing_ms":4.2}` |
| `push_index` | `{}` | `{"status":"ok","doc_count":N}` |
| `clear_index` | `{}` | `{"status":"ok"}` |

### Error shape

```json
{"error": "message"}
```

## Moss SDK calls (verified PyPI 1.6.0)

```python
client = MossClient(os.environ["MOSS_PROJECT_ID"], os.environ["MOSS_PROJECT_KEY"])
session = await client.session(index_name="documents")
await session.add_docs([DocumentInfo(id=path, text=content, metadata={"path": path, "filename": name})])
results = await session.query("query", QueryOptions(top_k=5, alpha=0.6))
await session.push_index() # optional cloud sync
```

## Text extraction

- Supported: `.md`, `.txt`, `.rtf`, `.html` (BeautifulSoup), `.pdf` (pypdf), `.docx` (python-docx)
- Skipped: `.notes`
- Chunks: ~1800 chars with 300 char overlap; IDs like `path#chunk-0001`

## Dev credentials

Priority: `MOSS_PROJECT_ID`/`MOSS_PROJECT_KEY` env → Keychain → repo `.env` via `DotEnvLoader`
10 changes: 10 additions & 0 deletions apps/moss-pikachu/.cursor/skills/moss-pikachu/pitfalls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Pitfalls

1. **Do not use Moss Swift SPM on macOS** — `Package.swift` targets iOS only
2. **Do not install Moss from GitHub main SDK** — no `SessionIndex`; use `pip install moss>=1.6.0`
3. **No Python disk save API** — use in-memory session + Swift file manifest for relaunch
4. **FSEvents permissions** — may need Full Disk Access; handle `start()` returning false gracefully
5. **App Sandbox blocks subprocess** — disable sandbox for MVP or add `com.apple.security.cs.allow-unsigned-executable-memory`
6. **Bundle resources** — `moss_worker.py` must be in Copy Bundle Resources; resolve via `Bundle.main.url(forResource:withExtension:)`
7. **@MainActor default** — Xcode 26 sets `SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor`; mark background work `nonisolated` or use `Task.detached`
8. **Carbon hotkey in sandbox** — works without sandbox; test on real hardware not just simulator
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
cd "$ROOT"
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install "moss>=1.6.0"
echo "Moss venv ready at $ROOT/.venv"
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail
PHASE="${1:-1}"
ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
cd "$ROOT"

case "$PHASE" in
1)
xcodebuild -project MossPikachu.xcodeproj -scheme MossPikachu -destination 'platform=macOS' build
echo "Phase 1: build OK — run in Xcode and verify menu bar + ⌘⇧M overlay"
;;
2)
test -f .venv/bin/python3 || { echo "Run ./scripts/setup-moss-venv.sh first"; exit 1; }
.venv/bin/python3 -c "from moss import MossClient; print('moss import OK')"
xcodebuild -project MossPikachu.xcodeproj -scheme MossPikachu -destination 'platform=macOS' build
echo "Phase 2: Moss SDK + build OK"
;;
3)
xcodebuild -project MossPikachu.xcodeproj -scheme MossPikachu -destination 'platform=macOS' build
echo "Phase 3: build OK — run end-to-end search test manually"
;;
*)
echo "Usage: validate-phase.sh [1|2|3]"
exit 1
;;
esac
31 changes: 31 additions & 0 deletions apps/moss-pikachu/.cursor/skills/moss-pikachu/ui-animations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# UI & Animations

## Search overlay sizing

- Width: 500pt
- Max height: 600pt (results scroll inside)
- Corner radius: 12pt
- Shadow: `radius: 20, y: 8, opacity: 0.25`

## Pikachu sizes

| Context | Size |
|---------|------|
| Menu bar | 32×32 pt |
| Search overlay | 64×64 pt |
| Hover scale | 1.05 |

## PetState animations

| State | Animation |
|-------|-----------|
| `idle` | breathe 1.0→1.02 (2s), blink every 5–8s random, tail ±2° every 3–5s |
| `searching` | tail wag ±15° (0.6s loop), thinking dots |
| `found(n)` | bounce 1.0→1.15 (0.4s ×2), ✨ sparkles |
| `notFound` | head tilt ±8° (1s), sad expression |

Use SwiftUI `.animation(.easeInOut, value:)` and `Timer` for random idle intervals.

## Search input debounce

200ms after last keystroke before calling `SearchService.search`.
2 changes: 2 additions & 0 deletions apps/moss-pikachu/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
MOSS_PROJECT_ID=your_project_id
MOSS_PROJECT_KEY=your_project_key
21 changes: 21 additions & 0 deletions apps/moss-pikachu/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Xcode
build/
DerivedData/
*.xcuserstate
xcuserdata/

# Python
.venv/
__pycache__/
*.pyc

# Moss / secrets
.env
.env.local

# macOS
.DS_Store

# Vendor submodule build artifacts
vendor/moss/**/target/
vendor/moss/**/.venv/
Loading
Loading