chore: scaffold Merlin Phase A repo

This commit is contained in:
pyr0ball 2026-04-26 21:03:19 -07:00
commit 072ee3f36c
9 changed files with 88 additions and 0 deletions

10
.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
__pycache__/
*.pyc
*.pyo
.env
.merlin/
models/*.pkl
dist/
*.egg-info/
node_modules/
app/dist/

0
merlin/__init__.py Normal file
View file

View file

56
merlin/config.py Normal file
View file

@ -0,0 +1,56 @@
"""
MerlinConfig load and save user gesture-to-action mappings.
Config file: ~/.merlin/config.yaml
If the file does not exist, defaults are used and written on first save.
"""
from __future__ import annotations
from dataclasses import dataclass, field, asdict
from pathlib import Path
from typing import Optional
import yaml
CONFIG_PATH = Path.home() / ".merlin" / "config.yaml"
DEFAULT_MAPPINGS: dict[str, str] = {
"left_blink": "left_click",
"right_blink": "right_click",
"both_blink": "double_click",
"head_nod": "key_enter",
"head_shake": "key_escape",
"head_tilt_left": "scroll_up",
"head_tilt_right": "scroll_down",
"open_palm": "key_space",
"pinch": "drag_start",
}
@dataclass
class MerlinConfig:
mappings: dict[str, str] = field(default_factory=lambda: dict(DEFAULT_MAPPINGS))
confidence_threshold: float = 0.80
camera_device: int = 0
dwell_ms: int = 800
blink_threshold: float = 0.20
@classmethod
def load(cls, path: Path = CONFIG_PATH) -> MerlinConfig:
if not path.exists():
return cls()
with path.open() as f:
data = yaml.safe_load(f) or {}
return cls(
mappings=data.get("mappings", DEFAULT_MAPPINGS),
confidence_threshold=float(data.get("confidence_threshold", 0.80)),
camera_device=int(data.get("camera_device", 0)),
dwell_ms=int(data.get("dwell_ms", 800)),
blink_threshold=float(data.get("blink_threshold", 0.20)),
)
def save(self, path: Path = CONFIG_PATH) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w") as f:
yaml.safe_dump(asdict(self), f, default_flow_style=False)

View file

22
pyproject.toml Normal file
View file

@ -0,0 +1,22 @@
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[project]
name = "merlin"
version = "0.1.0"
description = "Merlin — BCI and alternative input training harness (BSL 1.1)"
requires-python = ">=3.11"
dependencies = [
"circuitforge-core[gestures-mediapipe]",
"fastapi>=0.110",
"uvicorn[standard]>=0.29",
"scikit-learn>=1.4",
"joblib>=1.3",
"pyyaml>=6.0",
"pyautogui>=0.9",
]
[tool.setuptools.packages.find]
where = ["."]
include = ["merlin*"]

0
tests/__init__.py Normal file
View file

View file

View file