Webhook listener that monitors Gitea repos for BLIGHT: triggers in markdown files, processes them via Gemini 2.5 Flash-Lite, and writes results back in-place. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
186 lines
5.0 KiB
Markdown
186 lines
5.0 KiB
Markdown
# BLIGHT: CUE
|
|
|
|
A module in the **BLIGHT** ecosystem.
|
|
|
|
BLIGHT: CUE monitors Gitea repositories containing markdown files. When a push is received, it scans changed files for `BLIGHT:` trigger lines, sends the surrounding document to an AI model along with the instruction, and writes the result back to the file in-place — fully automated.
|
|
|
|
---
|
|
|
|
## How It Works
|
|
|
|
1. You write a `BLIGHT:` trigger anywhere in a markdown file and push it to Gitea.
|
|
2. Gitea sends a webhook POST to this server.
|
|
3. The server fetches the file, finds all `BLIGHT:` triggers, and processes them one by one.
|
|
4. Each trigger is replaced with the AI's response at the exact position of the trigger line.
|
|
5. The updated file is committed back to the repo automatically.
|
|
|
|
### Trigger Syntax
|
|
|
|
```
|
|
BLIGHT: <your instruction here>
|
|
```
|
|
|
|
Examples:
|
|
|
|
```markdown
|
|
This paragraph discusses は vs が in Japanese grammar.
|
|
|
|
BLIGHT: Explain the key differences between は and が based on the paragraph above.
|
|
|
|
## Next Section
|
|
```
|
|
|
|
```markdown
|
|
BLIGHT: Spell check this entire document and list any errors found.
|
|
```
|
|
|
|
```markdown
|
|
BLIGHT: Write a conclusion paragraph for this document.
|
|
```
|
|
|
|
### Failure Behavior
|
|
|
|
If the AI call fails after 3 attempts, the trigger is replaced with:
|
|
|
|
```html
|
|
<!-- BLIGHT_FAILED: your original instruction -->
|
|
```
|
|
|
|
You can re-trigger processing by editing the file to restore the original `BLIGHT:` line and pushing again.
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
- Python 3.10+
|
|
- Access to your Gitea instance (via Tailscale or local network)
|
|
- A [Google Gemini API key](https://aistudio.google.com/apikey)
|
|
- A Gitea personal access token with **repository read/write** permissions
|
|
|
|
---
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
# 1. Clone this repo onto the machine that will run the listener
|
|
git clone <this-repo-url>
|
|
cd Blight_Reader
|
|
|
|
# 2. Create and activate a virtual environment
|
|
python3 -m venv .venv
|
|
source .venv/bin/activate
|
|
|
|
# 3. Install dependencies
|
|
pip install -r requirements.txt
|
|
|
|
# 4. Copy the example env file and fill in your values
|
|
cp .env.example .env
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
Edit `.env` with your values:
|
|
|
|
| Variable | Description |
|
|
|---|---|
|
|
| `GITEA_URL` | Base URL of your Gitea instance, no trailing slash |
|
|
| `GITEA_TOKEN` | Personal access token from Gitea → Settings → Applications |
|
|
| `GEMINI_API_KEY` | API key from [Google AI Studio](https://aistudio.google.com/apikey) |
|
|
| `WEBHOOK_SECRET` | A secret string you choose — must match what you set in Gitea |
|
|
| `WEBHOOK_PORT` | Port the listener binds to (default: `5010`) |
|
|
|
|
---
|
|
|
|
## Running
|
|
|
|
```bash
|
|
python app.py
|
|
```
|
|
|
|
The server binds to `0.0.0.0:5010` (or your configured port). Keep it running as a service or in a screen/tmux session.
|
|
|
|
For production use, consider running it with a process manager:
|
|
|
|
```bash
|
|
# With systemd (example unit file)
|
|
# /etc/systemd/system/blight-cue.service
|
|
[Unit]
|
|
Description=BLIGHT: CUE webhook listener
|
|
After=network.target
|
|
|
|
[Service]
|
|
WorkingDirectory=/path/to/Blight_Reader
|
|
ExecStart=/path/to/Blight_Reader/.venv/bin/python app.py
|
|
Restart=always
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
> Replace `/path/to/Blight_Reader` with the actual path. The venv Python is at `.venv/bin/python` relative to the project root — create it with `python3 -m venv .venv && .venv/bin/pip install -r requirements.txt`.
|
|
|
|
---
|
|
|
|
## Registering the Webhook in Gitea
|
|
|
|
Do this for **each repository** you want BLIGHT: CUE to watch.
|
|
|
|
1. Open the repository in Gitea.
|
|
2. Go to **Settings** → **Webhooks** → **Add Webhook** → **Gitea**.
|
|
3. Set the fields:
|
|
- **Target URL**: `http://<your-machine-ip>:5010/webhook`
|
|
- **HTTP Method**: POST
|
|
- **Content Type**: `application/json`
|
|
- **Secret**: the same value as `WEBHOOK_SECRET` in your `.env`
|
|
- **Trigger On**: Push events only
|
|
4. Click **Add Webhook**, then use **Test Delivery** to verify connectivity.
|
|
|
|
---
|
|
|
|
## AI Provider
|
|
|
|
BLIGHT: CUE currently uses **Google Gemini 2.5 Flash-Lite** — the most cost-effective stable Gemini model (~$0.10/$0.40 per million tokens input/output).
|
|
|
|
### Adding a New Provider
|
|
|
|
All AI providers implement the `AIProvider` abstract base class in `ai/base.py`:
|
|
|
|
```python
|
|
from abc import ABC, abstractmethod
|
|
|
|
class AIProvider(ABC):
|
|
def complete(self, document: str, instruction: str) -> str:
|
|
...
|
|
```
|
|
|
|
To add a new provider (e.g. OpenRouter):
|
|
|
|
1. Create `ai/openrouter.py` and implement `AIProvider`.
|
|
2. In `processor.py`, replace `GeminiProvider()` with your new class.
|
|
|
|
---
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
Blight_Reader/
|
|
├── app.py # Flask webhook server
|
|
├── processor.py # Trigger scanning and replacement logic
|
|
├── gitea_client.py # Gitea REST API wrapper
|
|
├── config.py # Environment config loader
|
|
├── ai/
|
|
│ ├── base.py # AIProvider abstract base class
|
|
│ └── gemini.py # Gemini 2.5 Flash-Lite implementation
|
|
├── requirements.txt
|
|
├── .env.example
|
|
└── README.md
|
|
```
|
|
|
|
---
|
|
|
|
## Part of the BLIGHT Ecosystem
|
|
|
|
BLIGHT: CUE is one module in a larger modular system called **BLIGHT**. Each module is independently deployable and communicates through Gitea repositories as the shared data layer.
|