Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
14 changes: 13 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ GITHUB_EVENT_INTAKE_MODE=routing_websocket
# are set, so you normally do not need this.
# GH_AUTH_MODE=relay

# Advanced override for the GitHub user credential used only for visual-preview
# attachment uploads. An administrator's Web UI login can be captured when it
# uses a GitHub OAuth App token (`gho_`); GitHub's uploader rejects GitHub App
# user (`ghu_`) and installation (`ghs_`) tokens. Set this to override the Web
# UI flow with an OAuth App token, classic PAT, or fine-grained PAT for a user
# with write access to each target repository.
# GITHUB_VISUAL_PREVIEW_TOKEN=your_oauth_or_personal_access_token
# Optional dedicated encryption secret for the persisted OAuth grant. The
# default is SYSTEM_TASK_SECRET, falling back to SESSION_SECRET. Keep the value
# stable and identical for the API and worker.
# PROPR_CREDENTIAL_ENCRYPTION_KEY=generate-a-strong-secret-here

# --- Hosted UI tunnel (v1, optional) -----------------------------------------
# Expose this local stack's API to the hosted control plane at
# https://app.propr.dev through a Cloudflare Tunnel, so you can drive a
Expand Down Expand Up @@ -361,7 +373,7 @@ DASHBOARD_API_PORT=4000
# PROPR_ALLOW_INSECURE_LOCAL_WEB_PUSH=false

# Per-client request quotas. Defaults protect the general API (600/minute),
# OAuth/session endpoints (30/15 minutes), and direct webhooks (300/minute).
# OAuth initiation/callback endpoints (30/15 minutes), and direct webhooks (300/minute).
# Values must be positive integers; raise them only for measured trusted traffic.
# PROPR_API_RATE_LIMIT_MAX=600
# PROPR_API_RATE_LIMIT_WINDOW_MS=60000
Expand Down
14 changes: 14 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ FROM node:22-slim

WORKDIR /usr/src/app

ARG GH_VERSION=2.99.0
ARG TARGETARCH

# Install git, sudo, Docker tooling, and build tools for native modules
# (better-sqlite3). Debian's essential bsdutils package already provides
# script(1), used as the browser agent-login PTY bridge.
Expand All @@ -12,9 +15,20 @@ RUN apt-get update && apt-get install -y \
git \
sudo \
docker.io \
curl \
python3 \
make \
g++ \
&& gh_arch="${TARGETARCH:-amd64}" \
&& case "$gh_arch" in amd64|arm64) ;; *) echo "Unsupported GitHub CLI architecture: $gh_arch" >&2; exit 1 ;; esac \
&& gh_archive="gh_${GH_VERSION}_linux_${gh_arch}.tar.gz" \
&& curl -fsSLO "https://github.com/cli/cli/releases/download/v${GH_VERSION}/${gh_archive}" \
&& curl -fsSLO "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_checksums.txt" \
&& grep " ${gh_archive}$" "gh_${GH_VERSION}_checksums.txt" | sha256sum -c - \
&& tar -xzf "$gh_archive" \
&& install -m 0755 "gh_${GH_VERSION}_linux_${gh_arch}/bin/gh" /usr/local/bin/gh \
&& rm -rf "$gh_archive" "gh_${GH_VERSION}_checksums.txt" "gh_${GH_VERSION}_linux_${gh_arch}" \
&& gh --version \
&& rm -rf /var/lib/apt/lists/*

# Copy package files (including workspace packages)
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.agent
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ ARG CURL_VERSION_PREFIX=7.88.1-10+deb12u
# install falls back to the latest available version when this prefix no longer
# matches. That trades strict reproducibility for not breaking every build on a
# gh release; the fallback logs a note to stderr when it triggers.
ARG GH_VERSION_PREFIX=2.96.
ARG GH_VERSION_PREFIX=2.99.
ARG GIT_VERSION_PREFIX=1:2.39.5-0+deb12u
ARG GOSU_VERSION_PREFIX=1.14-1
ARG IPTABLES_VERSION_PREFIX=1.8.9-2
Expand Down
15 changes: 14 additions & 1 deletion Dockerfile.node
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
FROM node:22-alpine

ARG GH_VERSION=2.99.0
ARG TARGETARCH

# Install git, sudo, Docker tooling, script(1) for browser agent-login PTYs,
# curl, jq, and build tools for native modules (better-sqlite3)
# curl and jq are required for deploy-pr.sh script execution
# docker-cli-compose provides 'docker compose' (v2) command
RUN apk add --no-cache git sudo docker-cli docker-cli-compose curl jq util-linux-misc python3 make g++
RUN apk add --no-cache git sudo docker-cli docker-cli-compose curl jq util-linux-misc python3 make g++ \
&& gh_arch="${TARGETARCH:-amd64}" \
&& case "$gh_arch" in amd64|arm64) ;; *) echo "Unsupported GitHub CLI architecture: $gh_arch" >&2; exit 1 ;; esac \
&& gh_archive="gh_${GH_VERSION}_linux_${gh_arch}.tar.gz" \
&& curl -fsSLO "https://github.com/cli/cli/releases/download/v${GH_VERSION}/${gh_archive}" \
&& curl -fsSLO "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_checksums.txt" \
&& grep " ${gh_archive}$" "gh_${GH_VERSION}_checksums.txt" | sha256sum -c - \
&& tar -xzf "$gh_archive" \
&& install -m 0755 "gh_${GH_VERSION}_linux_${gh_arch}/bin/gh" /usr/local/bin/gh \
&& rm -rf "$gh_archive" "gh_${GH_VERSION}_checksums.txt" "gh_${GH_VERSION}_linux_${gh_arch}" \
&& gh --version

WORKDIR /usr/src/app

Expand Down
3 changes: 3 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ services:
- OPENCODE_CONFIG_PATH=${HOME:?HOME must be set}/.config/opencode
- VIBE_CONFIG_PATH=${HOME:?HOME must be set}/.vibe
- PROPR_MANAGED_CREDENTIALS_DIR=${HOME:?HOME must be set}/.propr/agent-credentials
# Optional override for the encrypted OAuth credential collected through
# the Web UI. App installation tokens cannot upload GitHub attachments.
- GITHUB_VISUAL_PREVIEW_TOKEN=${GITHUB_VISUAL_PREVIEW_TOKEN:-}
- PROPR_CONTAINERIZED=1
depends_on:
redis:
Expand Down
13 changes: 13 additions & 0 deletions docker/Dockerfile.app.prod
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,26 @@ FROM node:22-alpine AS runtime

WORKDIR /usr/src/app

ARG GH_VERSION=2.99.0
ARG TARGETARCH

# Runtime-only packages. No python/make/g++ — native modules were built in stage 1.
# git — simple-git operations
# sudo — worktree ownership changes
# docker-cli — spawning agent containers via mounted docker socket
# curl/jq — deploy-pr.sh script
# util-linux-misc — script(1), the PTY bridge for browser agent logins
RUN apk add --no-cache git sudo docker-cli curl jq tini util-linux-misc \
&& gh_arch="${TARGETARCH:-amd64}" \
&& case "$gh_arch" in amd64|arm64) ;; *) echo "Unsupported GitHub CLI architecture: $gh_arch" >&2; exit 1 ;; esac \
&& gh_archive="gh_${GH_VERSION}_linux_${gh_arch}.tar.gz" \
&& curl -fsSLO "https://github.com/cli/cli/releases/download/v${GH_VERSION}/${gh_archive}" \
&& curl -fsSLO "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_checksums.txt" \
&& grep " ${gh_archive}$" "gh_${GH_VERSION}_checksums.txt" | sha256sum -c - \
&& tar -xzf "$gh_archive" \
&& install -m 0755 "gh_${GH_VERSION}_linux_${gh_arch}/bin/gh" /usr/local/bin/gh \
&& rm -rf "$gh_archive" "gh_${GH_VERSION}_checksums.txt" "gh_${GH_VERSION}_linux_${gh_arch}" \
&& gh --version \
&& mkdir -p /tmp/git-processor \
&& git config --system --add safe.directory /usr/src/app/repos \
&& git config --system --add safe.directory /tmp/pr-worktrees \
Expand Down
1 change: 1 addition & 0 deletions docs/docs/features/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Use different coding agents without changing the rest of the workflow.
ProPR keeps follow-up work where the review already happens: the pull request.

- [PR automation and fine-tuning](./pr-followup.md): create pull requests automatically, then refine them through natural GitHub comments or slash-command workflows.
- [Visual previews](./visual-previews.md): attach focused image or video evidence when an implementation changes something users can see.
- [PR slash commands](./pr-commands.md): the command reference for `/review`, `/fix`, `/merge`, `/switch`, `/use`, and `/ultrafix`.
- [Branch configuration](./branch-config.md): repository-specific branch defaults and resolution rules.

Expand Down
1 change: 1 addition & 0 deletions docs/docs/features/pr-followup.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ When ProPR finishes an implementation task, it handles the GitHub plumbing aroun
- Pushes to GitHub
- Opens a pull request linked to the source issue
- Posts status back to GitHub
- Attaches focused [visual previews](./visual-previews.md) when the repository enables them and the change has a visible result
- Updates task and label state (`<trigger>-processing` → `<trigger>-done`, or `<trigger>-failed-*` on failure)

This keeps the agent focused on code while ProPR handles the repeatable workflow around the code.
Expand Down
4 changes: 4 additions & 0 deletions docs/docs/features/propr-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,13 +254,17 @@ propr repo remove owner/repo
propr repo toggle owner/repo --enable # Enable/disable monitoring
propr repo toggle owner/repo --auto-ci-followup # Enable failed-CI follow-up
propr repo toggle owner/repo --no-auto-ci-followup # Disable failed-CI follow-up
propr repo toggle owner/repo --visual-previews --preview-types image,video
propr repo toggle owner/repo --no-visual-previews
propr repo index owner/repo # Full reindex
propr repo index owner/repo --incremental # Incremental reindex
propr repo status # Indexing status for all repos
```

Automatic CI follow-up is configured per repository and is **off by default**. Enable it only for repositories whose CI failures are high-quality, trusted signals; noisy or flaky checks can otherwise create unnecessary follow-up work. `propr repo list` shows the current setting for every monitored repository.

Visual previews are also per-repository and **off by default**. `--preview-types` accepts `image`, `video`, or `image,video`; use `--preview-instructions` to add project-specific capture details. See [Visual Previews](./visual-previews.md) for generation and publication behavior.

## Agents

```bash
Expand Down
87 changes: 87 additions & 0 deletions docs/docs/features/visual-previews.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
title: Visual Previews
---

# Visual Previews

Visual previews let a ProPR implementation show its user-visible result directly in the generated pull request. The same policy applies to later follow-up commits, whose completion comments can include fresh media focused on that follow-up.

The feature is opt-in per repository. Existing repository configurations remain disabled after an upgrade.

GitHub attachment uploads require a GitHub OAuth App token (`gho_`) or personal
access token. GitHub's uploader rejects both GitHub App user (`ghu_`) and
installation (`ghs_`) tokens even though those tokens work for normal GitHub API
operations. When an instance administrator's Web UI login is backed by a GitHub
OAuth App, ProPR automatically stores its compatible credential, encrypted in
the shared database. Open **Settings → Visual preview uploads** to see which
account is connected or explicitly replace it with the current administrator
login. Normal GitHub API, commit, and pull-request operations continue to use
the GitHub App installation token.

When normal Web UI login uses a GitHub App, an administrator can instead paste
a personal access token in **Settings → Visual preview uploads**. ProPR validates
the token with GitHub and encrypts it before storing it. No CLI, callback URL, or
service restart is required. For a fine-grained token, choose the organization or
user that owns the repositories as the resource owner, include every
preview-enabled repository, and grant the repository permission **Pull requests:
Read and write**. GitHub adds read-only metadata access automatically; no other
repository permission is required. The token owner must have push access to the
repositories and must complete any organization approval or SAML SSO authorization.
Fine-grained tokens can target only one resource owner. If the repositories span
multiple owners, use a classic PAT with `repo`, or `public_repo` if every repository
is public. Settings links to GitHub's token form with the fine-grained permission
preselected.

Expiring OAuth credentials are refreshed on API startup and every 30 minutes
while the stack is running. Each successful refresh rotates the access and
refresh tokens, so an administrator does not need to sign in every six months
while the stack can keep refreshing them. A revoked grant, an expired unused
refresh token, or a changed encryption secret requires a fresh administrator
login. Personal access tokens are not refreshable OAuth grants; replace a
revoked or expired PAT in **Settings → Visual preview uploads**. As an advanced
server-managed alternative, configure `GITHUB_VISUAL_PREVIEW_TOKEN` with an
OAuth App token, classic PAT, or fine-grained PAT belonging to a user with write
access to every preview-enabled repository.

`propr setup` also reuses an upload-compatible token from an existing `gh` CLI
session when no working preview credential is already configured. GitHub CLI
does not expose a refresh token to ProPR, so an expired or revoked imported token
must be replaced in Settings or re-imported by running setup again.

## Configure A Repository

On **Repositories**, turn on **Visual previews** beneath the repository entry. Choose **Images**, **Videos**, or both, then optionally add capture instructions such as:

```text
Capture separate desktop and mobile views. Open the new settings dialog and focus the changed controls.
```

The setting is repository-wide. If the same repository has entries for multiple base branches, ProPR keeps their preview policy synchronized.

The CLI exposes the same policy:

```bash
propr repo add owner/repo --visual-previews --preview-types image,video \
--preview-instructions "Capture desktop and mobile views."
propr repo toggle owner/repo --visual-previews --preview-types image
propr repo toggle owner/repo --no-visual-previews
```

## What The Agent Captures

When enabled, the implementation agent evaluates the completed change:

- If the result is perceptible visually, it captures the changed state with relevant project tooling such as Playwright, Storybook, a browser, an emulator, or a project-native renderer.
- If the change has no visible result, it does not create placeholder media.
- Captures focus on the change rather than generic application screens and must not contain credentials, personal data, or unrelated content.
- If capture is blocked, the agent can recommend the concrete browser, emulator, or media tool that should be added to the agent image.

Agents generate files under the transient `.propr/previews/` runtime directory. Optional titles, descriptions, and tool recommendations are recorded in `.propr/previews/manifest.json`. Before committing, ProPR copies accepted files to worker-owned temporary storage and removes the runtime directory from the worktree. Preview files are therefore never included in the implementation commit.

Supported image formats are PNG, JPEG, GIF, SVG, and WebP. Supported video formats are MP4, MOV, and WebM; H.264 MP4 is the most broadly compatible choice. Each attachment must be smaller than 10 MB.

## Publication And Upload Failures

ProPR publishes previews as [GitHub attachments](https://cli.github.com/manual/gh_pr_edit) so images render inline and videos use GitHub's media presentation. For follow-ups, it uploads the media first and then updates the existing progress comment; it does not create a temporary second comment. ProPR verifies that every temporary local path was replaced with a hosted attachment URL, then deletes the temporary files. If upload or verification fails, ProPR publishes a text-only explanation; preview media is not added to Git as a fallback. When the failure is a missing, unsupported, expired, or rejected user credential, that explanation includes the exact Settings reconnection steps in the affected pull request.

Preview generation is evidence, not a replacement for automated tests. A preview failure does not discard an otherwise valid implementation; the PR explains missing tool support when the agent can identify it.
2 changes: 1 addition & 1 deletion docs/docs/features/web-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ These records are the heart of ProPR's observability — see [Observability And

## Repositories

**Repositories** (`/repositories`) manages the repos ProPR monitors — add, alias, set a base branch, enable/disable, reindex, hide, or delete. The selected repository opens a panel with four tabs:
**Repositories** (`/repositories`) manages the repos ProPR monitors — add, alias, set a base branch, enable/disable, configure [visual previews](./visual-previews.md), reindex, hide, or delete. Visual preview controls select image/video evidence and optional capture instructions for each repository. The selected repository opens a panel with four tabs:

- **Chat** — converse with the indexed repository;
- **Improve** — generate categorized improvement suggestions;
Expand Down
4 changes: 3 additions & 1 deletion docs/docs/operations/configuration-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ The backend authenticates to GitHub in one of three modes — `demo`, `relay`, o
| `HOST_GH_PRIVATE_KEY` | Unset | Absolute host path to the `.pem`. The CLI/launcher bind-mounts it read-only into the app containers and overrides `GH_PRIVATE_KEY_PATH`, so the key can live anywhere on the host. No `~`. | App mode via the `propr` CLI or launcher. |
| `GH_OAUTH_CLIENT_ID` / `GH_OAUTH_CLIENT_SECRET` | Placeholders | GitHub OAuth App credentials for Web UI login. | Always, for UI login. |
| `GH_OAUTH_CALLBACK_URL` | Derived: `<API host>/api/auth/github/callback` | OAuth callback served by the API. Leave commented so tunnel-mode derivation wins; an active localhost value is used as-is even in tunnel mode. Register the URL — derived or explicit — in your GitHub OAuth App. | Override only. |
| `GITHUB_VISUAL_PREVIEW_TOKEN` | Unset | Advanced override for the OAuth App token (`gho_`), classic PAT, or fine-grained PAT used only to upload visual-preview attachments. Administrators can normally paste a PAT in Settings instead, and `propr setup` imports a compatible `gh` CLI token when available. GitHub's uploader rejects GitHub App user (`ghu_`) and installation (`ghs_`) tokens. | Optional override. |
| `PROPR_CREDENTIAL_ENCRYPTION_KEY` | `SYSTEM_TASK_SECRET`, then `SESSION_SECRET` | Optional dedicated secret used to encrypt the persisted visual-preview OAuth grant. It must be identical in the API and worker containers and remain stable across restarts; changing it requires reconnecting the GitHub login. | Optional security isolation. |
| `SESSION_SECRET` | Placeholder | Signs browser session cookies. | Always. |
| `ENABLE_BEARER_AUTH` | `true` (any value except `false` enables it) | Bearer token auth for the CLI. Set `false` to allow session login only. | Optional. |
| `PROPR_DEMO_MODE` | `false` | `true`/`1` allows read-only access without GitHub OAuth and blocks all mutating API requests. Use a curated config/database for public demos. | Demo deployments. |
Expand All @@ -44,7 +46,7 @@ The backend authenticates to GitHub in one of three modes — `demo`, `relay`, o
| `WEB_PUSH_RETRY_BASE_MS` / `WEB_PUSH_RETRY_CAP_MS` | `30000` / `900000` | Base and cap for exponential retry scheduling after throttling, provider errors, or network failures. | Optional Web Push tuning. |
| `PROPR_ALLOW_INSECURE_LOCAL_WEB_PUSH` | `false` | Requests loopback HTTP Push enrollment for isolated local development. It is honored only outside production when `API_PUBLIC_URL` is unset/local or has a loopback host, and can be changed without migrating the stable schema. | Local browser development only. |
| `PROPR_API_RATE_LIMIT_MAX` / `PROPR_API_RATE_LIMIT_WINDOW_MS` | `600` / `60000` | Per-client quota and window (milliseconds) for all `/api` requests. | Optional tuning. |
| `PROPR_AUTH_RATE_LIMIT_MAX` / `PROPR_AUTH_RATE_LIMIT_WINDOW_MS` | `30` / `900000` | Additional, tighter per-client quota for OAuth and session endpoints. | Optional tuning. |
| `PROPR_AUTH_RATE_LIMIT_MAX` / `PROPR_AUTH_RATE_LIMIT_WINDOW_MS` | `30` / `900000` | Additional, tighter per-client quota for OAuth initiation and callback endpoints. | Optional tuning. |
| `PROPR_WEBHOOK_RATE_LIMIT_MAX` / `PROPR_WEBHOOK_RATE_LIMIT_WINDOW_MS` | `300` / `60000` | Per-client quota for direct webhook requests, applied before body parsing and signature verification. | Optional tuning in direct-webhook mode. |
| `PROPR_TRUSTED_PROXY_PEERS` | Unset; launcher-managed tunnel: reserved `self` mode | Comma-separated immediate proxy IPs, CIDRs, or `proxy-addr` names whose forwarded client IP and protocol are trusted. Unset ignores forwarding headers. The launcher injects `self` only for its managed sidecar sharing the API network namespace. Its broad `uniquelocal` name is accepted only when `API_PORT` is explicitly loopback-bound. | Reverse-proxy deployments; injected automatically for the managed tunnel. |
| `LOG_LEVEL` | `info` | Log verbosity across services. | Optional. |
Expand Down
1 change: 1 addition & 0 deletions docs/sidebars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const sidebars: SidebarsConfig = {
items: [
'features/pr-followup',
'features/pr-commands',
'features/visual-previews',
],
},
{
Expand Down
Loading
Loading