Skip to content

AZIO-126/az-desk

Repository files navigation

AZ Desk

AZ Desk is a two-way task and delivery board between an AI agent (Claude) and a human owner, plus a small agentic-loop engine that lets the agent act on the board on its own.

The agent writes task cards — status, deliverables, a plain-prose progress note. The owner opens the board (e.g. from a phone), taps a status, leaves a 👍/👎/🤔 verdict, or types a free-text note. Every feedback action is persisted and appended to a durable inbox. A worker tails that inbox and lets the agent act on the feedback and report back — so the owner's feedback visibly changes the board. That round trip is the whole point: it's a feedback loop, not a one-way to-do list.

This repository is an extracted, generalized version of a personal setup. All personal data, credentials, hardcoded hosts, and project-specific content have been removed; the loop that shipped with one bespoke project is replaced by a neutral example.

Architecture

                    ┌───────────────────────────────────────────┐
   owner's phone ──►│  board.html  (single-page UI, no build)    │
                    └───────────────┬───────────────────────────┘
                                    │  fetch /api/*
                    ┌───────────────▼───────────────────────────┐
                    │  server.py  (Flask)                        │
                    │   GET  /api/tasks      read the board      │
                    │   POST /api/feedback   status/sat/note/due │
                    │   POST /api/update     agent write-back    │
                    │   POST /api/quickadd   /api/smartadd       │
                    │   POST /api/project-chat /api/board-chat   │
                    │   single writer of tasks.json (atomic)     │
                    └───┬───────────────┬───────────────┬────────┘
        appends a line  │               │ appends       │ chat inbox lines
     feedback_inbox     │               │ inboxes       │
                    ┌───▼───┐   ┌────────▼──────┐  ┌─────▼──────────────┐
                    │receipt│   │  act_worker   │  │ board_chat_worker  │
                    │worker │   │ tails feedback│  │ project_chat_worker│
                    │(notify│   │ spawns claude │  │ drive the engine   │
                    │ ack)  │   │ writes back   │  │ on a chat message  │
                    └───────┘   └───────┬───────┘  └─────────┬──────────┘
                                        │ HTTP /api          │ run_*_message
                                        ▼                    ▼
                              ┌──────────────────────────────────────┐
                              │  az_desk/engine  (vendored)           │
                              │   loop_runner   gather→maker→gate→    │
                              │                 land→report→checkpoint│
                              │   loop_specs    per-project LoopSpec  │
                              │   project_memory packed, ranked memory│
                              │   maker != checker, fail-closed gate  │
                              └──────────────────────────────────────┘

Pieces:

  • server.py — the Flask board server. It is the only writer of tasks.json (atomic temp-file replace). Workers and the engine mutate the board only through its HTTP API, so there is one consistent write path.
  • board.html — a single self-contained page (no build step) the owner interacts with. It talks to the server with relative /api/... paths.
  • act_worker.py — tails the feedback inbox; for each actionable note it spawns a headless claude that does the safe, doable part and writes its progress back via /api/update. Maintains a per-task warm session so a follow-up note resumes prior context.
  • receipt_worker.py — sends the owner a quick "got it" receipt for each feedback action (optional; only does anything if you configure a notifier).
  • board_chat_worker.py / project_chat_worker.py — drive the loop engine when the owner chats with the whole board (the global manager) or with one project, respectively.
  • board_nudge.py — a daily focus digest + (optional, gated) event nudges, computed from board state. Optional notifier.
  • reminders_sync.py — an optional, macOS-only, dormant-by-default mirror of hard deadlines into Apple Reminders via a remindctl-compatible CLI you supply.
  • az_desk/engine/ — the vendored agentic-loop engine (self-contained; does not import any private package). A loop runs six stages — gather → maker → independent gate → land → report → checkpoint. The maker is a headless claude whose proposed board mutations are validated by a second, independent checker pass (maker ≠ checker), which is fail-closed and strictly subtractive. project_memory.py packs a per-project workspace memory under a character budget, ranking snippets with scikit-learn's TF-IDF (with a pure-Python fallback).

Feedback loop, end to end

  1. The owner taps a status / leaves a note on a card → POST /api/feedback.
  2. The server persists it to tasks.json and appends a line to feedback_inbox.jsonl.
  3. act_worker.py (if enabled) picks up the note, spawns a headless claude with a self-contained prompt, and the agent writes its progress back via POST /api/update.
  4. The board now shows the agent's reply in the card's thread.

Chatting with a project or the whole board works the same way, but the worker drives the loop engine (run_loop_message / run_board_message) instead of a one-shot prompt.

Setup

pip install -r requirements.txt

Python 3.9+ is fine. scikit-learn is optional (used for memory ranking; the engine falls back to a pure-Python ranker without it).

Seed the board from the sample data:

cp sample_tasks.json tasks.json

Run

python server.py            # serves the board on http://127.0.0.1:8770

Open http://127.0.0.1:8770/ in a browser. The board binds loopback by default. To reach it from another device, put it behind your own network boundary (a VPN such as Tailscale, an SSH tunnel, or a reverse proxy with auth) — there is no auth layer in this server, so never expose it on 0.0.0.0 to an untrusted network. Override the bind with BOARD_LISTEN_HOST / BOARD_LISTEN_PORT.

Workers (optional)

The workers are gated OFF by default so you can run them dormant and inspect the logs first.

# auto-act on feedback notes (spawns claude); requires the `claude` CLI on PATH
ACT_ENABLED=1 python act_worker.py

# drive the loop engine from the chat boxes
PROJECT_CHAT_ENABLED=1 python project_chat_worker.py
BOARD_CHAT_WORKER_ENABLED=1 python board_chat_worker.py

Configuration

Env var Used by Default Meaning
BOARD_LISTEN_HOST / BOARD_LISTEN_PORT server 127.0.0.1 / 8770 bind address
AZ_DESK_VAULT server, act_worker cwd root for deliverable file paths + spawned-agent cwd
AZ_DESK_BOARD_API / BOARD_BASE / BOARD_API_BASE workers, engine http://127.0.0.1:8770 board API base URL
AZ_DESK_HOME engine ~/.az-desk engine state / logs / project memory root
AZ_DESK_CLAUDE_CMD engine claude the claude command to spawn (a test seam)
AZ_DESK_AGENTS_DIR engine (unset) optional agent-role prompt catalog
AZ_DESK_NOTIFY_CMD / AZ_DESK_NOTIFY_USER workers, nudge (unset) optional outbound-notify CLI (--user-id <id> --text <text>); unset = log-only
ACT_ENABLED act_worker 0 spawn claude on feedback notes
PROJECT_CHAT_ENABLED / BOARD_CHAT_WORKER_ENABLED chat workers 0 drive the engine from chat
REMINDERS_ENABLED / REMINDCTL_BIN reminders_sync 0 / remindctl optional Apple Reminders mirror

The repo ships no notifier and no credentials. The notify-based pieces (receipt_worker, board_nudge) only do anything when you point AZ_DESK_NOTIFY_CMD at a CLI you supply; otherwise they log what they would have sent.

Tests

pytest

Tests run with no network and no real claude: the engine's maker/gate spawns are routed to a fake binary via AZ_DESK_CLAUDE_CMD, and every board/notify effect is a recording mock.

A note on provenance

This is an extracted and generalized version of a personal "delivery board" + loop engine. The original was tied to one user's projects, hosts, and credentials; all of that has been removed and replaced with neutral examples. The single example loop (az_desk/engine/loop_specs.py, prompts/example.py) reads a plain-text chat transcript and lands tasks on the board — it is a template you replace with your own gather hook and prompt pack.

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors