Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ Full table: [Dialog UX (humans)](docs/AGENTS.md#dialog-ux-humans).
|-------|------|----|-------|--------|
| Ubuntu 24.04 + GNOME + PipeWire | Cursor | Yes | Yes | **Verified** (2026-07) |
| Same stack | Claude Code | Yes | Text-only | **Verified** (2026-07) — [@jackghx](https://github.com/jackghx) |
| Windows 10/11 + tkinter | Cursor | Text | No | Phase 1 — **not yet reported** |
| Windows 10/11 + WebView2 | Cursor | Text (Nebula UI) | No | Phase 2 UI — **Anthony laptop** |
| Windows 10/11 + tkinter | Cursor | Text | No | Fallback if pywebview missing |
| macOS / headless CI | — | No | No | Unsupported / N/A |

More rows & how to report: [CONTRIBUTING.md](CONTRIBUTING.md).
Expand Down
41 changes: 21 additions & 20 deletions docs/WINDOWS.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
# Windows (Anthony / Cursor) — Phase 1 handoff
# Windows (Anthony / Cursor) — WebView2 Nebula dialog

Target user: **Anthony** ([@TheRealCheese](https://github.com/TheRealCheese)).
Goal: text-only `ask_multiple_choice` on **Cursor for Windows** without WSL.

Canonical install steps: this file (and a one-line pointer from the
[README](../README.md#quick-start-linux)).

## UI backend

| Priority | Backend | Notes |
| --- | --- | --- |
| 1 (default) | **WebView2 + pywebview** | Nebula-styled floating dialog (`win_webview_ask.py`) |
| 2 | tkinter | Fallback (`win_list_ask.py`) if pywebview missing |

Force with env: `ASK_QUESTION_WIN_UI=webview` or `ASK_QUESTION_WIN_UI=tk`.

## Checklist for Anthony

1. Python 3.12+ from python.org with **tcl/tk** → `python -c "import tkinter; print('ok')"`.
1. Python 3.12+ (python.org). Edge WebView2 is already on Windows 11.
2. Install uv → `where uv` (absolute path to `uv.exe`).
3. `git clone https://github.com/DynamicDevices/ask-question-mcp.git` then:
3. In the checkout:
```bat
uv sync
uv run ask-question-install --host cursor --skill
```
(or `git pull` + the same if you already have a clone).
4. Cursor → **Developer: Reload Window**.
5. Ask the agent: call **`check_setup`** (expect `ready.ui` / `ready.text_mcq` true; `audio_mode` text_only).
6. Smoke **`ask_multiple_choice`** — dialog should appear on top; pick an option.
5. Ask the agent: call **`check_setup`** (expect `ready.ui` / `ready.text_mcq` true;
`webview` ok; `audio_mode` text_only).
6. Smoke **`ask_multiple_choice`** — Nebula WebView2 dialog on top; pick an option.
7. Smoke **keyboard** — labels show `1 · …`; press **2**, wait for OK to arm,
**Enter**. **Esc** cancels. Footer hint: `1–8 select · Enter OK · Esc cancel`.
8. Smoke **Something else** — every MCQ should include a freeform row / entry; typing
Expand All @@ -37,30 +46,22 @@ Manual mcp.json edit is still fine if you skip the installer — use absolute

## Behaviour parity (vs Linux)

Shared path (`zenity_ask` → `win_list_ask.py`):

| Behaviour | Windows Phase 1 |
| Behaviour | Windows |
| --- | --- |
| Something else always offered | Yes (same as Linux; `allow_other` ignored) |
| Danger mark **⛔** + confirm arm | Yes (`danger_arm.py`) |
| Danger banner wording | **⛔ Confirm** + lead ask (pink banner) |
| Lead / detail (ask visible; tall referent scrolls) | Yes (`split_lead_detail`) |
| Lead / detail (ask visible; tall referent scrolls) | Yes (`split_lead_detail` on tk; WebView body scrolls) |
| Red OK on danger | Yes |
| Voice / duck / STT | No (text-only) |
| Image / images preview | No (ignored; text-only) |
| Gtk footer / scroll layout fixes | N/A (tkinter layout) |
| Image / images preview | No (ignored; text-only for now) |
| 1–8 hotkeys + Enter / Esc | Yes |
| Remember size/position | Yes (`prefs.window`) |
| Scrollable options | Yes |
| Aesthetic | Nebula glass WebView2 (frameless); tk fallback; optional Edge `--app` |

## Out of scope (Phase 1)
## Out of scope

- Spoken questions / mic answers / media duck
- WSL as the supported path
- macOS

## After Anthony verifies

Maintainers: update README Tested platforms Windows row to **Verified**, and
optionally add a `VERIFIED_PLATFORMS` entry with `"system": "windows"` in
[`platform_info.py`](../src/ask_question_mcp/platform_info.py).
- Hosting the dialog *inside* the Cursor chat chrome (MCP has no custom panel API yet)
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ authors = [
]
dependencies = [
"mcp[cli]>=1.9.0",
"pywebview>=5.0",
]

[project.urls]
Expand Down
9 changes: 8 additions & 1 deletion scripts/test_windows_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@
from ask_question_mcp.zenity_ask import (
_WIN_ENTRY_ASK,
_WIN_LIST_ASK,
_WIN_WEBVIEW_ASK,
_WIN_WEBVIEW_ENTRY_ASK,
ui_backend,
)


def main() -> None:
assert _WIN_LIST_ASK.is_file(), _WIN_LIST_ASK
assert _WIN_ENTRY_ASK.is_file(), _WIN_ENTRY_ASK
assert _WIN_WEBVIEW_ASK.is_file(), _WIN_WEBVIEW_ASK
assert _WIN_WEBVIEW_ENTRY_ASK.is_file(), _WIN_WEBVIEW_ENTRY_ASK

if sys.platform == "win32":
assert ui_backend() == "win"
Expand Down Expand Up @@ -75,7 +79,10 @@ def main() -> None:
assert caps.audio_mode == "text_only"
assert caps.speak_active is False
assert caps.listen_active is False
assert any("Windows Phase 1" in n for n in caps.notes)
assert any(
"Windows" in n and ("WebView2" in n or "Phase 1" in n or "tkinter" in n)
for n in caps.notes
)

# Ensure Linux UI-ready still requires DISPLAY when not Windows.
if sys.platform != "win32":
Expand Down
97 changes: 39 additions & 58 deletions skills/ask-multiple-choice/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
---
name: ask-multiple-choice
description: >-
Prefer desktop MCP ask_multiple_choice for decision forks — not markdown
A/B/C. Use when choosing or confirming. Pass image=/images= when the human
must judge a still in the dialog.
Desktop MCP ask_multiple_choice for every decision fork — Anthony's opinion
when stuck, choosing paths, confirming work, or picking options. Prefer this
over markdown A/B/C or host AskQuestion. Supports multi-select and optional
image= / images= previews. Use whenever the ask-question MCP is available
and a human choice is needed.
---

# Ask multiple choice (desktop MCP)

**Need-band:** freedom — faster decisions without chat A/B/C noise.
Cursor stand-in for Claude Code **AskUserQuestion**. **Need-band:** freedom —
faster decisions without chat A/B/C noise.

## When (auto)
## When (auto — do not wait to be asked)

Any **decision fork**: ship/wait, pick a path, confirm irreversible work,
choose among options the human must decide.
Any fork that needs **Anthony's opinion**, including when you are **stuck**:

- ship / wait / pick a path
- confirm irreversible or risky work
- choose among concrete options
- several answers may apply together → multi-select
- preference unknown — ask; do not guess

## Do

Expand All @@ -27,63 +35,36 @@ choose among options the human must decide.
(always fully visible); put Command/To+body/path **before** meta notes.
Do **not** dump process templates, PATTERN blocks, or long meta into routine
forks. No meta about dialogs/voice.
4. **Permission / action asks (Alex 2026-07-30):** state **what** Briar will do
**and why** (one sentence each is enough). Opaque “run Task agents?” /
“proceed?” without purpose is not enough — Alex must understand the aim.
4. **Permission / action asks:** state **what** will happen **and why** (one
sentence each is enough). Opaque “proceed?” without purpose is not enough.
Pattern: `mcq-permission-what-and-why`.
5. Mark preferred only as **`Label (recommended)`** + **`recommended_id`**.
6. **`dangerous=true`** for irreversible / high-risk forks.
7. **Images the human must judge** (Alex loves this — signed-off 2026-08-03):
pass **`image=`** (one path / `file://` URI) or **`images=`** (list, max 4).
Chat `Read` of a PNG does **not** put pixels in the MCQ — agents **must**
pass the path into the dialog. Linux Gtk: opens large on the **primary**
usable workarea (not the largest secondary); human can **click the preview**
(large ↔ compact ~320px) and **maximize** (header button or **F**).
**P0 — multi-image must never exceed primary usable resolution** (stack
shares one height budget / scrolls inside). Text-only MCQs stay compact.
Pattern: `mcq-with-image`.
8. Wait for the JSON result. On cancel → stop. On freeform → use **`freeform_text`**.

Humans use the dialog keyboard (**1–8**, Enter, Esc; **F** maximize when images);
do not put hotkey instructions in `question`. Detail: repo `docs/AGENTS.md`
(Dialog UX).

## Voice — who is “I”? (Alex 2026-07-30)

MCQs must not use ambiguous **I** / **you** for actions.

| Role | How to refer |
|------|----------------|
| Human | **Alex** (or “Alex will…”) |
| This assistant | **Briar** (or “Briar will…”) |

**Do:** `Briar will restart the webhook` · `Alex approves the send` · option
labels like `Briar sends now` / `Alex will edit first`.
**Don't:** `I'll capture…` / `you refresh…` / `I mean the agent…` when either
party could be “I”.

Casual chat outside MCQs may still use normal I/you; **MCQ question + option
labels** stay role-named. Pattern: `mcq-named-roles-alex-briar`.
5. Mark preferred only as **`Label (recommended)`** + **`recommended_id`**
(or **`recommended_ids`** for multi).
6. Set **`allow_multiple=true`** when more than one option can be correct together
(checklist). Default single-select otherwise.
7. **`dangerous=true`** for irreversible / high-risk forks.
8. **Images the human must judge:** pass **`image=`** (one path / `file://`) or
**`images=`** (list). Chat `Read` of a PNG does **not** put pixels in the MCQ.
Linux Gtk shows the preview in-dialog; Windows WebView path may ignore images
until wired.
9. Wait for the JSON result.
- Cancel → **stop** (do not invent a choice).
- Freeform → honour **`freeform_text`**.
- Multi → use **`ids`** / **`labels`**.

Humans use dialog keys (**1–8**, Enter, Esc); do not put hotkey text in
`question`. Detail: repo `docs/AGENTS.md` (Dialog UX).

## Don't

- Markdown A/B/C, numbered lists, or host AskQuestion when this MCP is available
- Desktop MCQ for clarifications / next steps when the ask originated over
**WhatsApp admin inbound** — keep those on WhatsApp (Charlize voice or short
text) per `cursor-pa-whatsapp` SOUL; desktop MCQ is for Cursor-session forks
not originated on WA (unless Alex opts in). P0 send-gates to third parties
may still use desktop MCQ.
- Asking Alex to judge a still that exists only in chat when the dialog can take
**`image=`** / **`images=`**
- “Send now?” / “Ship it?” with no body/path when the human has not seen the draft
- Permission MCQs that name a tool/action but omit **why** (Task agents, long
scans, enable MCP, restart services, etc.)
- Stuffing PATTERN/PROPOSAL/OWNS walls into every MCQ
- Soft MCQs before a send-gate (“draft OK?”, “ready?”, “shall Briar send?”) —
draft in chat, then **one** send-gate only (`email-one-send-gate`)
- Asking the human to judge a still that exists only in chat when the dialog can
take **`image=`** / **`images=`**
- Soft MCQs before a real send-gate — draft in chat, then one confirm
- `check_setup` before routine MCQs (only first enable, dialog failure, or before voice)
- Invent a choice after `cancelled: true`
- Ambiguous **I/you** in MCQ question or option labels (use Alex / Briar)
- Skip the dialog because the skill wasn't @-mentioned — the always-on user rule
still applies when the MCP is loaded

## Setup (humans)

Expand All @@ -92,4 +73,4 @@ cd /path/to/ask-question-mcp && uv sync
uv run ask-question-install --host cursor --skill
```

Then reload the host. Detail: repo `docs/AGENTS.md`.
Then reload the host. Windows: see `docs/WINDOWS.md`. Detail: `docs/AGENTS.md`.
Loading