Skip to content
Adams-404Public

About

Nudge: Gemma 4 desk + laptop companion for coding agents on Arduino UNO Q

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

49 Commits

Folders and files

Repository files navigation

◉ Nudge

A desk + laptop companion for coding agents on the Arduino UNO Q.
Gemma 3 1B runs locally on the board as the triage brain. A physical Allow/Deny button on your desk guards your machine.

Arduino UNO Q Gemma 3 1B Linux Wayland / X11 License: MIT Hacktoberfest 2026

Nudge Banner


What is Nudge?

When coding agents (Claude Code, Cursor, Codex, Antigravity) run in the background, you face a constant dilemma: babysit terminal windows or tab away and stall out waiting for an approval prompt.

Nudge moves your agent's permission gate from a hidden terminal tab to your physical desk:

  • Desk (Arduino UNO Q): Meets Pip, a vigilant little desk creature. His 13x8 LED-matrix face reacts to your agent's state in real-time. Gemma 3 1B runs directly on the board's Linux MPU via the App Lab arduino:llm brick, triaging incoming events and keeping the gate closed until you press the physical green Allow button.
  • Laptop (Sidecar & Island): A top-edge island for Linux (Wayland / X11) and a universal hook watcher. It intercepts agent actions, strips confidential environment variables, compresses commands into safe 1-liners, and binds approvals strictly to unique request IDs.

Gemma 3 1B is the brain on the board. Your laptop is the hands with an allowlist.


Architecture

flowchart LR
  subgraph laptop [Laptop / Workstation]
    Hooks["Agent Hooks (Cursor / Claude / Codex)"]
    Island["Top-Edge Island (GtkLayerShell / Dock)"]
    Sidecar["Nudge Sidecar (Port 7474)"]
    Hooks -->|"JSON hook event"| Sidecar
    Sidecar <-->|"Allow / Deny UI"| Island
  end

  subgraph board [Arduino UNO Q]
    Py["Python Agent (Port 7000 / 8766)"]
    Gemma["Local Gemma 3 1B (llama.cpp)"]
    MCU["MCU Sketch (Bridge RPC)"]
    Matrix["13x8 LED Matrix Face"]
    Buttons["Physical Allow / Deny Pins"]
    Web["Web UI (Canvas 2D Pip)"]

    Py -->|"Triage prompt"| Gemma
    Gemma -->|"Validated Tool JSON"| Py
    Py <-->|"RouterBridge"| MCU
    MCU --> Matrix
    Buttons --> MCU
    Py --> Web
  end

  Sidecar -->|"Sanitized Event (POST /api/event)"| Py
  MCU -->|"Physical Button Press"| Py
  Py -->|"Bound Decision (POST /api/decision)"| Sidecar
  Sidecar -->|"Exit 0 (Allow) or 1 (Deny)"| Hooks
Loading

The AI Brain: Gemma 3 1B on the Board

Pip's brain is Gemma 3 1B, running locally on the Arduino UNO Q via the App Lab arduino:llm brick.

       RAW AGENT HOOK
  "git push origin main"
            │
            ▼
┌────────────────────────┐
│  Laptop Sidecar (Hands)│ ── Strips secrets, truncates to 200 chars
└───────────┬────────────┘
            │ POST /api/event
            ▼
┌────────────────────────┐
│  Arduino Linux MPU     │
│  ┌──────────────────┐  │
│  │ Gemma 3 1B      │  │ ── Runs locally on board via llama.cpp
│  │ (arduino:llm)    │  │
│  └────────┬─────────┘  │
│           │ Strictly Validated Tool JSON
│  ┌────────▼─────────┐  │
│  │ Python Triage    │  │ ── Drops shell commands, validates schema
│  └────────┬─────────┘  │
└───────────┼────────────┘
            │ RouterBridge RPC
            ▼
┌────────────────────────┐
│  Zephyr MCU            │
│  • 13x8 LED Matrix     │ ── Lights up animated emoji faces & blinks
│  • Physical Buttons    │ ── Tactile Allow / Deny approval
└────────────────────────┘

Where the Brain Lives in Code

  • Model Declaration: applab/app.yaml binds arduino:llm (llamacpp:gemma-3-1b-it-Q4_0) and arduino:web_ui on port 7000.
  • Triage & Validation Engine: applab/python/main.py hosts the GemmaTriage class. It prompts Gemma 3 1B with Pip's character persona and parses tool JSON.
  • Strict Allowlist: The model can only emit request_approval, notify, focus_session, and open_island. It has no shell execution tool.
  • Local Testing: You can test with any local LLM server on your laptop (e.g. Ollama via export LLM_BASE_URL="http://localhost:11434/v1" and export LLM_MODEL="gemma2:2b").

Does the Arduino Light Up Cute Emoji Faces? Yes! ◉‿◉

The Arduino UNO Q has a built-in 13×8 LED Matrix (104 LEDs on the board). Pip's facial expressions light up dynamically on the physical hardware via applab/sketch/sketch.ino.

Here is how the physical LEDs light up for each mood:

1. idle (Gentle Smile & Random Blink Cycle)

Pip stands guard on your desk. Every 3–5 seconds, his eyelids drop for 180ms to blink naturally:

Open Eyes:                     Blink (180ms):
. . . . . . . . . . . . .     . . . . . . . . . . . .
. . ■ ■ . . . . . ■ ■ . .     . . . . . . . . . . . .
. . ■ ■ . . . . . ■ ■ . .     . . ■ ■ ■ . . . ■ ■ ■ .
. . . . . . . . . . . . .     . . . . . . . . . . . .
. ■ . . . . . . . . . ■ .     . ■ . . . . . . . . ■ .
. . ■ ■ ■ ■ ■ ■ ■ ■ ■ . .     . . ■ ■ ■ ■ ■ ■ ■ ■ ■ .
. . . . . . . . . . . . .     . . . . . . . . . . . .
. . . . . . . . . . . . .     . . . . . . . . . . . .

2. needs_you (Attention Alert / Pulsing Flash)

When your agent asks for permission (e.g. git push), the LED matrix alternates every 350ms between two frames, flashing an urgent attention signal across your desk:

Frame A (Pulsing Eyes):       Frame B (Flash):
. . . . . . . . . . . . .     ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
. ■ ■ ■ ■ . . . ■ ■ ■ ■ .     ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
. ■ ■ ■ ■ . . . ■ ■ ■ ■ .     ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
. . . . . . . . . . . . .     ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
. . . . . . . . . . . . .     ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
. ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ .     ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
. . . . . . . . . . . . .     ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
. . . . . . . . . . . . .     ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■

3. happy (Curved ^^ Victory Eyes)

When you press the green Allow button or your agent completes its work:

. . . . . . . . . . . . .
. . ■ ■ . . . . . ■ ■ . .
. ■ . . ■ . . . ■ . . ■ .
■ . . . . ■ ■ . . . . . ■
. ■ . . . . . . . . . ■ .
. . ■ ■ ■ ■ ■ ■ ■ ■ ■ . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .

4. annoyed (Skeptical Flat Brows - -)

When a command is denied or Pip is poked while working:

. . . . . . . . . . . . .
. ■ ■ ■ ■ . . . ■ ■ ■ ■ .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ .
. . . . . . . . . . . . .
. . . . . . . . . . . . .

5. dizzy (Spinning Crossed Eyes X X)

Triggered when you poke Pip 3+ times in rapid succession:

. ■ . . . ■ . ■ . . . ■ .
. . ■ . ■ . . . ■ . ■ . .
. ■ . . . ■ . ■ . . . ■ .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . ■ . ■ . ■ . ■ . ■ . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .

6. asleep (Closed Lids with z Z)

When no agent sessions have been active:

. . . . . . . . . . ■ ■ ■
. . . . . . . . . . . . ■
. . . . . . . . . . ■ ■ ■
. . ■ ■ ■ . . . ■ ■ ■ . .
. . . . . . . . . . . . .
. ■ . . . . . . . . . ■ .
. . ■ ■ ■ ■ ■ ■ ■ ■ ■ . .
. . . . . . . . . . . . .

Mood State Reference

Mood Expression Behavior
idle Gentle blinking, curved smile Waiting for coding agent sessions.
peek Shifted pupils, perked ears Detects an agent session initializing.
watching Focused wide pupils Tracking steps (running tests, reading files).
needs_you Pulsing alert, wide pleading eyes Permission requested. LED matrix pulses, waiting for physical Allow button.
happy Cheerful curved eyes ^^, bouncing Permission granted or session successfully completed!
annoyed Flat skeptical brows - - Permission denied or invalid action.
dizzy Spinning crossed eyes X X Triggered by 3+ rapid physical or virtual pokes!
asleep Closed eyelids with floating z Z Idle timeout when no sessions have run.

Quickstart (60 Seconds)

1. Desk: Import in Arduino App Lab

  1. Download dist/nudge-applab.zip (or build with python3 scripts/build_applab_zip.py).
  2. Open Arduino App Lab on your UNO Q.
  3. Click Apps → Import Apps and select nudge-applab.zip.
  4. Click Run. Pip's Web UI will be live at http://<board-ip>:7000.

2. Laptop: Start Sidecar & Island

On your Linux laptop:

# Start the background sidecar daemon
python3 laptop/sidecar.py

# (Optional) Open the top-of-screen island
python3 laptop/island.py

The sidecar will display a 6-digit pairing PIN (e.g. [ 482910 ]).

3. Pair

Open Pip's Web UI at http://localhost:7000 (or your UNO Q IP), click Pair Laptop, and enter the 6-digit PIN. The board and laptop exchange a cryptographic session token over your local LAN.

4. Run the 60-Second Demo

Want to see the full flow without configuring agent hooks?

python3 laptop/demo_session.py

Or click Try 60s Demo in Pip's Web UI. Watch Pip detect the agent, pulse when git push is requested, and jump with joy when you tap Allow!


Agent Hook Setup

To link your real coding agents to Nudge, point their permission hooks to laptop/hooks/nudge-hook.

Claude Code

Add to ~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "/absolute/path/to/nudge/laptop/hooks/nudge-hook"
      }
    ]
  }
}

Cursor & Custom Agents

In your Cursor custom hook or CLI runner:

echo '{"kind":"approval","agent":"cursor","command":"npm publish","request_id":"uuid"}' | ./laptop/hooks/nudge-hook

Fail-Open Guarantee: If Nudge is not running, nudge-hook exits immediately with code 0 so your work is never blocked.


Security Model

  • Request ID Binding: Every approval request is bound to a unique request_id. Stale decisions or late button presses are dropped.
  • Sanitized Payloads: The laptop sidecar strips secrets, tokens, and truncates commands to 200 characters before transmitting to the board.
  • No Remote Shell: Gemma 3 1B has no execution shell tool. It can only request approvals or dispatch allowlisted UI actions (notify, focus_session, open_island).
  • LAN-Only Pairing: The sidecar only listens locally. Pairing is protected by a rotating 6-digit PIN and token authentication.

Repository Structure

├── applab/                    # Importable Arduino App Lab Package
│   ├── app.yaml              # App definition (bricks: arduino:llm, arduino:web_ui)
│   ├── python/main.py        # Python agent: Gemma 3 1B triage, Bridge RPC, Web UI
│   ├── sketch/               # MCU code: 13x8 LED Matrix face & button debouncing
│   │   ├── sketch.ino
│   │   └── sketch.yaml
│   └── assets/               # Web UI (Liquid blob Pip: 16-point Bezier jelly, dark CSS)
│       ├── index.html
│       ├── style.css
│       └── pip.js
├── laptop/                    # Laptop Sidecar & Top-Edge Island
│   ├── sidecar.py            # Local daemon: hook sanitizer & LAN router
│   ├── island.py             # Top-edge desktop island (Wayland / X11)
│   ├── demo_session.py       # Standalone 60s demo simulator
│   └── hooks/
│       └── nudge-hook        # CLI hook script for Claude Code / Cursor / Codex
├── scripts/
│   └── build_applab_zip.py   # Packaging script generating dist/nudge-applab.zip
├── dist/                      # Packaged release archives
│   ├── nudge-applab.zip
│   └── nudge-applab.zip.sha256
├── protocol.md               # Wire protocol specifications
├── DEMO.md                   # 60-second judge demo script
├── DEV_POST.md               # DEV Community Hacktoberfest submission article
├── LICENSE                   # MIT License
└── README.md

Credits & Inspiration

  • Concept Inspiration: Inspired by Coucou by Louis Raillé. Nudge is a clean-room, original implementation built specifically for physical Arduino hardware, local Gemma 3 1B inference, and the Arduino App Lab ecosystem.
  • Character: Pip is an original procedural Canvas 2D character created for Nudge.
  • Author: Built for Hacktoberfest 2026 (Week 2: Side Hustle).

License

MIT © 2026 Nudge Contributors. See LICENSE for details.

About

Nudge: Gemma 4 desk + laptop companion for coding agents on Arduino UNO Q

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages