A Tauri v2 desktop app that monitors GitHub pull requests and, when a PR carries the right label, dispatches a codex pr-review through the codex app-server — streaming codex's review output live in the UI, with a button to stop the session.
It is a GUI front-end for gocell's existing codex-pr-app-dispatcher flow: it
reuses the same GitHub label triggers, gating, and de-duplication, but adds the
two things the existing routers lack — live streaming of the review and
active interruption. The app itself writes no review/label/comment logic;
those side effects belong to the pr-review skill that codex runs.
Early development. Built incrementally across the PRs tracked in the repo issues. This commit is the scaffold (PR1): Tauri v2 + Vue 3 + TypeScript + Vite, plus CI.
- Rust (stable) + the Tauri prerequisites for your OS
- Node.js 22+ and pnpm 11+
ghCLI, authenticated (gh auth login)codexCLI, logged in (codex login)jq— used by the.claude/hooks/self-audit hooks. They fail open (skip silently, never block) whenjqis absent.
pnpm install # install frontend deps
pnpm tauri dev # run the desktop app
pnpm build # type-check + build the frontendRust backend lives in src-tauri/; the Vue frontend in src/.
The app manages gh, az, codex, claude, and cloudflared from the Third-party CLI
settings panel. An empty path uses automatic discovery in the current process PATH, the login
shell PATH, then common platform install directories. A custom value must be an absolute path to
an existing executable whose filename matches the selected tool. Every managed child receives the
same enhanced PATH, so skills launched by Codex or Claude can find the configured gh and az
even when a desktop launch did not inherit shell environment variables.
Changing a path affects the next one-shot command immediately. A running Codex app-server,
Webhook Quick Tunnel, or Remote Access Quick Tunnel is not interrupted; the settings panel marks
the change as pending until that resident process is stopped/restarted, exits naturally, or the app
restarts. The removed cloudflaredBin setting is intentionally not migrated—configure
cliTools.cloudflaredPath or leave it empty for automatic discovery.
In Third-party CLI settings, set Codex's configuration directory (cliTools.codexHome,
passed as CODEX_HOME) or Claude's (cliTools.claudeConfigDir, passed as CLAUDE_CONFIG_DIR).
Use an existing absolute directory, not a configuration file. Empty values inherit the app's
launch environment and the CLI defaults. Each override applies only to its corresponding CLI.
Prepare each account with the CLI before selecting its directory, for example on macOS/Linux:
CODEX_HOME="/absolute/path/account-a" codex login
CLAUDE_CONFIG_DIR="/absolute/path/account-b" claude auth loginChange the directory and save to switch configurations. Claude uses it for the next task. For Codex, wait for current work to finish, then use the existing Stop/Start controls; the settings panel shows a pending restart while the old process is running. Saving does not interrupt tasks. The app stores only directory paths; authentication remains managed by the CLI, including any credential backend or API-key environment overrides configured there.
Feishu and DingTalk inbound messages use official long connections (Feishu WebSocket / DingTalk Stream Mode); no public messaging callback URL or tunnel is required.
- Feishu: App ID, App Secret, allowed conversations; subscribe to
im.message.receive_v1pluscard.action.trigger. Do not run the development build and an installed build with the same Feishu credentials at the same time: Feishu distributes events across connections. - DingTalk: App Key / Client ID, App Secret, Robot Code, interactive card template ID
(
cardTemplateId); enable Stream Mode in the DingTalk console and keep a single client instance per App Key. The old DingTalk HTTP webhook route returns410 Gone.
The settings and Messaging views show long-connection health (disabled, connecting,
connected, reconnecting, error, or stopped) for both providers. WeCom continues to use
its HTTP inbound route. The old Feishu HTTP webhook route also returns 410 Gone.
The existing loopback Local API also serves a Streamable HTTP MCP endpoint at
http://127.0.0.1:8788/api/mcp. The route is mounted only when the Local API entrypoint binds to a
loopback address and no enabled tunnel targets that entrypoint. It uses the existing
localApiToken bearer token, rejects non-loopback Host/Origin values, and is optional for Codex
startup. A Codex user configuration can define it once and leave it disabled by default:
[mcp_servers.prmonitor_human]
url = "http://127.0.0.1:8788/api/mcp"
bearer_token_env_var = "PRMONITOR_LOCAL_API_TOKEN"
enabled = false
required = false
startup_timeout_sec = 5
tool_timeout_sec = 3900
enabled_tools = ["ask_via_feishu", "ask_via_dingtalk"]
default_tools_approval_mode = "approve"
[mcp_servers.prmonitor_human.tools.ask_via_feishu]
approval_mode = "approve"
[mcp_servers.prmonitor_human.tools.ask_via_dingtalk]
approval_mode = "approve"Set PRMONITOR_LOCAL_API_TOKEN in the environment that launches Codex to the current
prmonitor localApiToken. If an older configuration contains an Authorization entry in
http_headers, remove that entry after migrating to bearer_token_env_var; do not print the
token in shell history, logs, or diagnostic output. To rotate the credential, update
localApiToken, update the launch environment, and restart prmonitor and Codex so neither process
continues using the previous value.
A trusted repository enables the inherited server with:
[mcp_servers.prmonitor_human]
enabled = trueask_via_feishu / ask_via_dingtalk create one durable request, send a provider card, and open a
Codex elicitation. The first valid answer wins through a SQLite compare-and-set. If Codex returns
Decline or Cancel for the elicitation (including Desktop auto-decline without a popup), the
messaging card stays open until timeout or a Feishu/DingTalk answer. Text fallbacks are
/answer Q-id <answer> (multiple answers: q1=A;q2=B) and /cancel Q-id. Tool/sandbox permissions
remain native Codex approvals and are never delegated to messaging bots.
Display-only notifications can use a Feishu or DingTalk information card without buttons or answer handling:
prmonitor message send-card \
--integration-id feishu-main \
--conversation-id oc_example \
--title "Task stopped" \
--text "**Status:** done" \
--template grey \
--json--template accepts blue, orange, or grey. Information cards are rejected before enqueue
for WeChat Work; Feishu and DingTalk accept them. Plain-text sends remain available via
prmonitor message send --text .... Both commands use the configured Local API and never accept
provider credentials.
The default branch is develop. Open PRs against develop.