hermes-agent/optional-skills/health/neuroskill-bci/references/api.md
teknium1 8a3e7e15c6 feat(skills): add NeuroSkill BCI integration as optional built-in skill
Complete rewrite of the neuroskill-bci skill based on actual source material
from the NeuroSkill desktop app and NeuroLoop CLI repos. Supersedes PR #708.

Key improvements over #708:
- All CLI commands verified against actual NeuroSkill/NeuroLoop source
- Added --json flag usage throughout (critical for reliable parsing)
- Fixed metric formulas: Focus = σ(β/(α+θ)), Relaxation = σ(α/(β+θ))
- Scores are 0-1 scale (not 0-100 as in #708)
- Added all 40+ metrics: FAA, TAR, BAR, TBR, APF, SNR, coherence,
  consciousness (LZC, wakefulness, integration), complexity (PE, HFD, DFA),
  cardiac (RMSSD, SDNN, pNN50, LF/HF, stress index, SpO2),
  motion (stillness, blinks, jaw clenches, nods, shakes)
- Added all missing CLI subcommands: session, search-labels, interactive,
  listen, umap, calibrate, timer, notify, raw
- Protocols sourced from actual NeuroLoop protocol repertoire (70+)
  organized by category (attention, stress, emotional, sleep, somatic,
  digital, dietary, motivation)
- Added full WebSocket/HTTP API reference with all endpoints and
  JSON response formats
- Fixed gamma range: 30-50 Hz (not 30-100)
- Added signal quality per electrode with thresholds
- Added composite state patterns (flow, fatigue, anxiety, creative, etc.)
- Added ZUNA embedding documentation
- Placed as optional built-in skill (not bundled by default)

Files:
- optional-skills/health/DESCRIPTION.md (new category)
- optional-skills/health/neuroskill-bci/SKILL.md (main skill)
- optional-skills/health/neuroskill-bci/references/metrics.md
- optional-skills/health/neuroskill-bci/references/protocols.md
- optional-skills/health/neuroskill-bci/references/api.md

Refs: #694, #708
2026-03-12 21:56:07 -07:00

8.2 KiB

NeuroSkill WebSocket & HTTP API Reference

NeuroSkill runs a local server (default port 8375) discoverable via mDNS (_skill._tcp). It exposes both WebSocket and HTTP endpoints.


Server Discovery

# Auto-discovery (built into the CLI — usually just works)
npx neuroskill status --json

# Manual port discovery
NEURO_PORT=$(lsof -i -n -P | grep neuroskill | grep LISTEN | awk '{print $9}' | cut -d: -f2 | head -1)
echo "NeuroSkill on port: $NEURO_PORT"

The CLI auto-discovers the port. Use --port <N> to override.


HTTP REST Endpoints

Universal Command Tunnel

# POST / — accepts any command as JSON
curl -s -X POST http://127.0.0.1:8375/ \
  -H "Content-Type: application/json" \
  -d '{"command":"status"}'

Convenience Endpoints

Method Endpoint Description
GET /v1/status System status
GET /v1/sessions List sessions
POST /v1/label Create label
POST /v1/search ANN search
POST /v1/compare A/B comparison
POST /v1/sleep Sleep staging
POST /v1/notify OS notification
POST /v1/say Text-to-speech
POST /v1/calibrate Open calibration
POST /v1/timer Open focus timer
GET /v1/dnd Get DND status
POST /v1/dnd Force DND on/off
GET /v1/calibrations List calibration profiles
POST /v1/calibrations Create profile
GET /v1/calibrations/{id} Get profile
PATCH /v1/calibrations/{id} Update profile
DELETE /v1/calibrations/{id} Delete profile

WebSocket Events (Broadcast)

Connect to ws://127.0.0.1:8375/ to receive real-time events:

EXG (Raw EEG Samples)

{"event": "EXG", "electrode": 0, "samples": [12.3, -4.1, ...], "timestamp": 1740412800.512}

PPG (Photoplethysmography)

{"event": "PPG", "channel": 0, "samples": [...], "timestamp": 1740412800.512}

IMU (Inertial Measurement Unit)

{"event": "IMU", "ax": 0.01, "ay": -0.02, "az": 9.81, "gx": 0.1, "gy": -0.05, "gz": 0.02}

Scores (Computed Metrics)

{
  "event": "scores",
  "focus": 0.70, "relaxation": 0.40, "engagement": 0.60,
  "rel_delta": 0.28, "rel_theta": 0.18, "rel_alpha": 0.32,
  "rel_beta": 0.17, "hr": 68.2, "snr": 14.3
}

EXG Bands (Spectral Analysis)

{"event": "EXG-bands", "channels": [...], "faa": 0.12}

Labels

{"event": "label", "label_id": 42, "text": "meditation start", "created_at": 1740413100}

Device Status

{"event": "muse-status", "state": "connected"}

JSON Response Formats

status

{
  "command": "status", "ok": true,
  "device": {
    "state": "connected",     // "connected" | "connecting" | "disconnected"
    "name": "Muse-A1B2",
    "battery": 73,
    "firmware": "1.3.4",
    "EXG_samples": 195840,
    "ppg_samples": 30600,
    "imu_samples": 122400
  },
  "session": {
    "start_utc": 1740412800,
    "duration_secs": 1847,
    "n_epochs": 369
  },
  "signal_quality": {
    "tp9": 0.95, "af7": 0.88, "af8": 0.91, "tp10": 0.97
  },
  "scores": {
    "focus": 0.70, "relaxation": 0.40, "engagement": 0.60,
    "meditation": 0.52, "mood": 0.55, "cognitive_load": 0.33,
    "drowsiness": 0.10, "hr": 68.2, "snr": 14.3, "stillness": 0.88,
    "bands": { "rel_delta": 0.28, "rel_theta": 0.18, "rel_alpha": 0.32, "rel_beta": 0.17, "rel_gamma": 0.05 },
    "faa": 0.042, "tar": 0.56, "bar": 0.53, "tbr": 1.06,
    "apf": 10.1, "coherence": 0.614, "mu_suppression": 0.031
  },
  "embeddings": { "today": 342, "total": 14820, "recording_days": 31 },
  "labels": { "total": 58, "recent": [{"id": 42, "text": "meditation start", "created_at": 1740413100}] },
  "sleep": { "total_epochs": 1054, "wake_epochs": 134, "n1_epochs": 89, "n2_epochs": 421, "n3_epochs": 298, "rem_epochs": 112, "epoch_secs": 5 },
  "history": { "total_sessions": 63, "recording_days": 31, "current_streak_days": 7, "total_recording_hours": 94.2, "longest_session_min": 187, "avg_session_min": 89 }
}

sessions

{
  "command": "sessions", "ok": true,
  "sessions": [
    { "day": "20260224", "start_utc": 1740412800, "end_utc": 1740415510, "n_epochs": 541 },
    { "day": "20260223", "start_utc": 1740380100, "end_utc": 1740382665, "n_epochs": 513 }
  ]
}

session (single session breakdown)

{
  "ok": true,
  "metrics": { "focus": 0.70, "relaxation": 0.40, "n_epochs": 541 /* ... ~50 metrics */ },
  "first":   { "focus": 0.64 /* first-half averages */ },
  "second":  { "focus": 0.76 /* second-half averages */ },
  "trends":  { "focus": "up", "relaxation": "down" /* "up" | "down" | "flat" */ }
}

compare (A/B comparison)

{
  "command": "compare", "ok": true,
  "insights": {
    "deltas": {
      "focus": { "a": 0.62, "b": 0.71, "abs": 0.09, "pct": 14.5, "direction": "up" },
      "relaxation": { "a": 0.45, "b": 0.38, "abs": -0.07, "pct": -15.6, "direction": "down" }
    },
    "improved": ["focus", "engagement"],
    "declined": ["relaxation"]
  },
  "sleep_a": { /* sleep summary for session A */ },
  "sleep_b": { /* sleep summary for session B */ },
  "umap": { "job_id": "abc123" }
}

search (ANN similarity)

{
  "command": "search", "ok": true,
  "result": {
    "results": [{
      "neighbors": [{ "distance": 0.12, "metadata": {"device": "Muse-A1B2", "date": "20260223"} }]
    }],
    "analysis": {
      "distance_stats": { "mean": 0.15, "min": 0.08, "max": 0.42 },
      "temporal_distribution": { /* hour-of-day distribution */ },
      "top_days": [["20260223", 5], ["20260222", 3]]
    }
  }
}

sleep (sleep staging)

{
  "command": "sleep", "ok": true,
  "summary": { "total_epochs": 1054, "wake_epochs": 134, "n1_epochs": 89, "n2_epochs": 421, "n3_epochs": 298, "rem_epochs": 112, "epoch_secs": 5 },
  "analysis": { "efficiency_pct": 87.3, "onset_latency_min": 12.5, "rem_latency_min": 65.0, "bouts": { /* wake/n3/rem bout counts and durations */ } },
  "epochs": [{ "utc": 1740380100, "stage": 0, "rel_delta": 0.15, "rel_theta": 0.22, "rel_alpha": 0.38, "rel_beta": 0.20 }]
}

label

{"command": "label", "ok": true, "label_id": 42}
{
  "command": "search-labels", "ok": true,
  "results": [{
    "text": "deep focus block",
    "EXG_metrics": { "focus": 0.82, "relaxation": 0.35, "engagement": 0.75, "hr": 65.0, "mood": 0.60 },
    "EXG_start": 1740412800, "EXG_end": 1740412805,
    "created_at": 1740412802,
    "similarity": 0.92
  }]
}

umap (3D projection)

{
  "command": "umap", "ok": true,
  "result": {
    "points": [{ "x": 1.23, "y": -0.45, "z": 2.01, "session": "a", "utc": 1740412800 }],
    "analysis": {
      "separation_score": 1.84,
      "inter_cluster_distance": 2.31,
      "intra_spread_a": 0.82, "intra_spread_b": 0.94,
      "centroid_a": [1.23, -0.45, 2.01],
      "centroid_b": [-0.87, 1.34, -1.22]
    }
  }
}

Useful jq Snippets

# Get just focus score
npx neuroskill status --json | jq '.scores.focus'

# Get all band powers
npx neuroskill status --json | jq '.scores.bands'

# Check device battery
npx neuroskill status --json | jq '.device.battery'

# Get signal quality
npx neuroskill status --json | jq '.signal_quality'

# Find improving metrics after a session
npx neuroskill session 0 --json | jq '[.trends | to_entries[] | select(.value == "up") | .key]'

# Sort comparison deltas by improvement
npx neuroskill compare --json | jq '.insights.deltas | to_entries | sort_by(.value.pct) | reverse'

# Get sleep efficiency
npx neuroskill sleep --json | jq '.analysis.efficiency_pct'

# Find closest neural match
npx neuroskill search --json | jq '[.result.results[].neighbors[]] | sort_by(.distance) | .[0]'

# Extract TBR from labeled stress moments
npx neuroskill search-labels "stress" --json | jq '[.results[].EXG_metrics.tbr]'

# Get session timestamps for manual compare
npx neuroskill sessions --json | jq '{start: .sessions[0].start_utc, end: .sessions[0].end_utc}'

Data Storage

  • Local database: ~/.skill/YYYYMMDD/ (SQLite + HNSW index)
  • ZUNA embeddings: 128-D vectors, 5-second epochs
  • Labels: Stored in SQLite, indexed with bge-small-en-v1.5 embeddings
  • All data is local — nothing is sent to external servers