{"id":50276956,"url":"https://github.com/telegraphic-dev/jean-ci","last_synced_at":"2026-05-27T21:03:05.101Z","repository":{"id":344356507,"uuid":"1167983835","full_name":"telegraphic-dev/jean-ci","owner":"telegraphic-dev","description":"GitHub webhook handler for Jean (OpenClaw)","archived":false,"fork":false,"pushed_at":"2026-05-27T12:22:43.000Z","size":701,"stargazers_count":0,"open_issues_count":6,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-27T14:17:50.300Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/telegraphic-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-26T22:28:07.000Z","updated_at":"2026-05-27T12:22:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/telegraphic-dev/jean-ci","commit_stats":null,"previous_names":["telegraphic-dev/jean-ci"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/telegraphic-dev/jean-ci","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/telegraphic-dev%2Fjean-ci","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/telegraphic-dev%2Fjean-ci/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/telegraphic-dev%2Fjean-ci/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/telegraphic-dev%2Fjean-ci/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/telegraphic-dev","download_url":"https://codeload.github.com/telegraphic-dev/jean-ci/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/telegraphic-dev%2Fjean-ci/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33583399,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-27T02:00:06.184Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-27T21:03:03.600Z","updated_at":"2026-05-27T21:03:05.095Z","avatar_url":"https://github.com/telegraphic-dev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# jean-ci\n\nGitHub webhook handler for automated PR reviews with LLM assistance.\n\n## Features\n\n\u003e OSS readiness is tracked in `docs/oss-readiness.md` (private-first rollout).\n\n- **Automated PR Reviews**: Run the built-in `Code Review` on every PR\n- **Customizable Default Review**: Edit the always-on default review prompt in the admin UI\n- **Additive Per-Repo Checks**: Add `.jean-ci/pr-checks/*.md` files for extra checks without replacing the default review\n- **GitHub Checks Integration**: Results appear as nested checks on PRs\n- **Admin Dashboard**: GitHub OAuth protected management interface\n\n## Architecture\n\n```\nPR Opened → Webhook → jean-ci → OpenClaw Gateway → LLM\n                         ↓\n                  GitHub Checks API\n                         ↓\n                  ✅ Global Standards\n                  ✅ security.md\n                  ✅ style.md\n```\n\n## PR Reviews Quickstart\n\n1. Copy `.env.example` to `.env` and set the GitHub App + OpenClaw gateway values.\n2. Install the GitHub App with **Checks**, **Contents**, **Pull requests**, and **Metadata** permissions.\n3. Optional: add one or more structured prompt files under `.jean-ci/pr-checks/*.md` for repo-specific checks.\n4. Open or update a pull request, or comment `/review` on an existing PR.\n\nMinimal starter prompt:\n\n```markdown\n# Security Review\n\n## Purpose\nCatch blocking security regressions before merge.\n\n## Review Instructions\nReview the PR diff for exposed secrets, missing authorization checks, unsafe input handling, and injection risks.\nOnly fail for merge-blocking findings.\n\n## Verdict Criteria\n- **FAIL** if the diff introduces a real security vulnerability or exposes secrets.\n- **PASS** if no blocking security issues are present in the diff.\n```\n\nImportant behavior:\n- `jean-ci / Code Review` is always created and always runs when PR review is enabled.\n- `.jean-ci/pr-checks/*.md` adds extra checks; it does not disable or replace the built-in review.\n- Fast prompt validation applies to custom repo checks. Existing admin-configured default review prompts keep running, so upgrades do not break the built-in review path.\n\nPrompt requirements for custom checks:\n- title\n- `## Purpose`\n- `## Review Instructions`\n- `## Verdict Criteria`\n- explicit `PASS` and `FAIL` conditions\n\nStarter prompt library:\n- `docs/prompt-library/security.md`\n- `docs/prompt-library/tests-and-docs.md`\n- `docs/prompt-library/migration-safety.md`\n\nFull walkthrough: `docs/pr-review-quickstart.md`\n\n## Container release channels\n\n- `ghcr.io/telegraphic-dev/jean-ci:dev` → published from every push to `main`\n- semver tags (`vX.Y.Z`) publish stable images and update `latest`\n- release pipeline also publishes SBOM, provenance, and cosign signatures\n\nSee `docs/release-engineering.md` for the release flow and runbook.\n\n## Setup\n\n### Easy local setup (Docker Compose)\n\n```bash\nmake bootstrap\n# edit .env and replace the required GitHub/OpenClaw placeholders\nmake doctor\ndocker compose up -d --build\n```\n\nThis starts:\n- `app` on `http://localhost:3000`\n- `postgres` as an internal Compose service (not published on the host by default)\n\nIf you are upgrading an existing deployment, run the manual override migration before starting a build that expects the new columns:\n\n```bash\npsql \"$DATABASE_URL\" -f scripts/migrate-add-manual-review-override-columns.sql\n```\n\nHelpful commands:\n- `make bootstrap` — create `.env`, generate local secrets, and sync a URL-encoded `DATABASE_URL` from `POSTGRES_*` values\n- `make doctor` — check Docker/Compose and fail until required GitHub/OpenClaw values are replaced with real values\n- `make up` / `make down` / `make logs`\n\nThe stock easy-setup flow is bash + Docker Compose only. No Python dependency is required.\n\nWhat `make bootstrap` does **not** do:\n- it does **not** invent fake GitHub App credentials for you\n- it does **not** invent a real OpenClaw gateway token\n- therefore `make doctor` will still fail until you replace those placeholders in `.env`\n\n### 1. Environment Variables\n\nStart from the template:\n\n```bash\ncp .env.example .env\n```\n\nThen set values:\n\n```bash\n# GitHub App\nGITHUB_APP_ID=your_app_id\nGITHUB_WEBHOOK_SECRET=your_webhook_secret\n# Default Docker Compose setup expects the private key inline as base64.\nGITHUB_APP_PRIVATE_KEY_B64=base64_encoded_private_key\n\n# GitHub OAuth (for admin UI)\nGITHUB_CLIENT_ID=your_oauth_client_id\nGITHUB_CLIENT_SECRET=your_oauth_client_secret\n\n# Admin access (your GitHub user ID)\nADMIN_GITHUB_ID=your_github_user_id\n\n# Public app URL (used for GitHub links / webhook forwarding)\nBASE_URL=https://jean-ci.example.com\nNEXT_PUBLIC_BASE_URL=https://jean-ci.example.com\n\n# OpenClaw Gateway\nOPENCLAW_GATEWAY_URL=https://openclaw.example.com\nOPENCLAW_GATEWAY_TOKEN=your_gateway_token\nOPENCLAW_GATEWAY_PUBLIC_URL=https://openclaw.example.com\nOPENCLAW_AGENT_ID=qa\n# Optional but recommended: use a dedicated jean-ci operator/device token,\n# not a personal token. If the gateway returns pairing/token-drift auth details,\n# jean-ci now surfaces actionable recovery guidance in logs/errors.\n\n# Optional Coolify integration\nCOOLIFY_URL=https://coolify.example.com\nCOOLIFY_DASHBOARD_URL=https://coolify.example.com\nCOOLIFY_TOKEN=your_coolify_token\nDEFAULT_DEPLOYMENT_DOMAIN=apps.example.com\n\n# Optional Paperclip integration (mark linked issues done when PRs merge)\nPAPERCLIP_API_URL=https://paperclip.example.com\nPAPERCLIP_API_KEY=your_paperclip_api_key\n\n# Data storage\nDATA_DIR=/data\n```\n\nIf you want to load the GitHub App private key from a file path instead of base64:\n- that is supported for non-Compose/manual deployments\n- the default `docker-compose.yml` does **not** mount a host key file into the container\n- for the stock easy-setup flow, use `GITHUB_APP_PRIVATE_KEY_B64`\n\n### 2. GitHub App Permissions\n\nRequired permissions:\n- **Actions**: Read (for successful `workflow_run` fallback deploy checks)\n- **Checks**: Read \u0026 Write\n- **Contents**: Read (for fetching `.jean-ci/` files)\n- **Pull requests**: Read\n- **Packages**: Read (for GHCR publish verification after build workflows finish)\n- **Metadata**: Read\n\nSubscribe to events:\n- `pull_request`\n- `pull_request_review`\n- `pull_request_review_comment`\n- `issue_comment`\n- `check_suite`\n- `installation`\n- `installation_repositories`\n- `registry_package`\n- `workflow_run`\n\n### 3. Custom PR Checks\n\nAdd markdown files to `.jean-ci/pr-checks/` in your repository:\n\n```\n.jean-ci/\n  pr-checks/\n    security.md    # Security review prompt\n    style.md       # Code style review prompt\n    tests.md       # Test coverage review prompt\n```\n\nEach file becomes a separate GitHub Check with its own ✅/❌ status.\n\nExample `security.md`:\n```markdown\nReview this PR for security issues:\n\n- SQL injection vulnerabilities\n- XSS attacks\n- Exposed secrets or credentials\n- Unsafe deserialization\n- Missing input validation\n\nReport any findings with specific line numbers.\n```\n\n## Admin Dashboard\n\nAccess at `/admin` after signing in with GitHub.\n\nFeatures:\n- Edit global PR review prompt\n- Enable/disable PR reviews per repository\n- View recent webhook events\n\n## Public REST API\n\njean-ci now exposes a token-protected public API so external services can read operational data without direct database access.\n\n- OpenAPI spec: `/api/public/openapi.json`\n- Scope: public token API only (`/api/public/v1/*`), not admin/session-authenticated routes under `/api/**`\n- Versioned endpoints: `/api/public/v1/*`\n- Auth: `Authorization: Bearer \u003ctoken\u003e`\n\n### Public endpoints\n\n- `GET /api/public/openapi.json`\n- `GET /api/public/v1/health`\n- `POST /api/public/v1/local-review`\n- `GET /api/public/v1/stats`\n- `GET /api/public/v1/repos`\n- `GET /api/public/v1/checks?page=1\u0026limit=50\u0026repo=owner/repo`\n- `GET /api/public/v1/pipelines?page=1\u0026limit=20\u0026repo=owner/repo`\n- `GET /api/public/v1/events?page=1\u0026limit=50\u0026eventType=workflow_run\u0026repo=owner/repo`\n- `GET /api/public/v1/tasks?view=summary`\n- `GET /api/public/v1/tasks?view=events\u0026page=1\u0026limit=50\u0026repo=owner/repo\u0026task=nightly`\n- `GET /api/public/v1/repos/{owner}/{repo}`\n- `GET /api/public/v1/repos/{owner}/{repo}/counts`\n- `GET /api/public/v1/repos/{owner}/{repo}/checks?page=1\u0026limit=50`\n- `GET /api/public/v1/repos/{owner}/{repo}/pipelines?page=1\u0026limit=20`\n- `GET /api/public/v1/repos/{owner}/{repo}/events?page=1\u0026limit=50`\n- `GET /api/public/v1/repos/{owner}/{repo}/deployments?page=1\u0026limit=50`\n\n### Admin-authenticated API endpoints\n\nThese endpoints are session-authenticated admin routes under `/api/**` and are intentionally outside the public OpenAPI document at `/api/public/openapi.json`.\n\n#### Token management\n\n- `GET /api/tokens` list issued tokens (hashed at rest; token value is not returned)\n- `POST /api/tokens` create a token (plain token is returned once)\n- `DELETE /api/tokens/{id}` revoke a token\n\n#### Check run override\n\n- `POST /api/checks/{id}/override`\n  - auth: logged-in admin session\n  - body: `{ \"reason\": \"...\" }`\n  - success: records a manual override in jean-ci only\n  - does not update the GitHub check run or submit a GitHub approval review\n\n### Local review execution\n\n`POST /api/public/v1/local-review` lets a local client send a repo slug, unified diff, and a git `headSha`/`ref`. jean-ci loads `.jean-ci/pr-checks/*.md` from git at that revision and runs them against the caller-provided local diff.\n\nExample:\n\n```bash\ncurl -X POST \"$JEAN_CI_URL/api/public/v1/local-review\" \\\n  -H \"Authorization: Bearer $JEAN_CI_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d @- \u003c\u003c'JSON'\n{\n  \"repo\": \"telegraphic-dev/jean-ci\",\n  \"title\": \"Local diff review\",\n  \"body\": \"Run jean-ci against my working tree\",\n  \"headSha\": \"327049e\",\n  \"diff\": \"diff --git a/file b/file\\n...\",\n  \"selectedChecks\": [\"api-openapi-parity\"]\n}\nJSON\n```\n\nNotes:\n- Use an existing public API token or one captured when created; token values are shown only once and are not visible again in the UI.\n- The caller supplies the local diff, but review prompts come only from git (`.jean-ci/pr-checks/*.md`) at the provided `headSha`/`ref`.\n- `repo` must be `owner/repo`; `headSha` or `ref` is required.\n- `selectedChecks` only filters git-backed checks; the built-in `Code Review` always runs.\n- Request bodies are size-limited before parsing; oversized requests are rejected with HTTP 400.\n- Internal execution failures return a generic HTTP 500 error body from this public endpoint.\n- The public OpenAPI document at `/api/public/openapi.json` includes this endpoint.\n\nOptional bootstrap token(s) can be set via env vars:\n\n- `PUBLIC_API_TOKEN`\n- `PUBLIC_API_TOKENS` (comma-separated)\n\n## Coolify Auto-Deploy\n\njean-ci can automatically deploy to Coolify when new container images are published to GHCR.\n\n## Paperclip PR Sync\n\nIf `PAPERCLIP_API_URL` and `PAPERCLIP_API_KEY` are set, jean-ci also watches merged GitHub pull requests and marks linked Paperclip issues as `done`.\n\nSupported link formats inside the PR body/title:\n- `https://paperclip.../issues/\u003cissue-uuid\u003e`\n- `https://paperclip.../\u003ccompany\u003e/issues/\u003cissue-id\u003e`\n- `Paperclip issue: \u003cissue-uuid\u003e`\n- `Paperclip issue: \u003cissue-id\u003e` (for example `THE-88`)\n- `\u003c!-- paperclip-issue-id:\u003cissue-uuid\u003e --\u003e`\n- `\u003c!-- paperclip-issue-id:\u003cissue-id\u003e --\u003e`\n\nIdentifier-based example:\n\n```text\nPR body: Paperclip issue: THE-91\nPR body: https://paperclip.telegraphic.app/THE/issues/THE-91\n```\n\nWhen a PR is closed without merging, no Paperclip update is sent.\n\n### Setup\n\n1. **Add a deployment config** to your repository.\n\nPreferred new format: `.jean-ci/deployments.yml`\n\n```yaml\n# jean-ci deployment config\ndeployments:\n  - provider: coolify\n    package: ghcr.io/your-org/your-repo\n    coolify_app: your-coolify-app-uuid\n    environment: production\n```\n\nReview-only / no-op mode:\n\n```yaml\ndeployments:\n  - provider: noop\n    package: ghcr.io/your-org/your-repo\n    environment: review-only\n```\n\nLegacy `.jean-ci/coolify.yml` is still supported for compatibility:\n\n```yaml\n# jean-ci Coolify Deployment Config\ndeployments:\n  - package: ghcr.io/your-org/your-repo\n    coolify_app: your-coolify-app-uuid\n    environment: production\n```\n\n2. **Configure GitHub Actions** to build and push to GHCR on push to main.\n\n3. **Install the jean-ci GitHub App** on your repository.\n\nWhen a new image is published to GHCR, jean-ci receives the `registry_package` webhook and triggers a Coolify deployment. As a fallback, when a default-branch build workflow completes successfully, jean-ci checks whether a matching `sha-\u003ccommit\u003e` GHCR tag exists and triggers the same deployment path if the package event was missed.\n\n### How It Works\n\njean-ci is **buildpack-agnostic** — it doesn't know or care whether your Coolify app uses `dockerfile`, `dockerimage`, or `dockercompose`. It simply:\n\n1. Receives `registry_package` webhook from GitHub, or a successful default-branch `workflow_run` completion with a matching GHCR `sha-\u003ccommit\u003e` tag\n2. Matches the package URL to a `coolify_app` UUID in your `.jean-ci/coolify.yml`\n3. Calls `POST /applications/{uuid}/restart` on Coolify API\n4. Coolify handles the rest based on its own app configuration\n\nThis means the **buildpack is configured in Coolify UI**, not in `coolify.yml`. The `coolify.yml` is just a mapping from GHCR package → Coolify app UUID.\n\n### Docker Compose Support\n\nFor apps that need volumes or custom networking, use Coolify's `dockercompose` buildpack:\n\n1. **Set build_pack to `dockercompose`** in Coolify UI\n\n2. **Add `docker-compose.yml`** to your repo:\n\n```yaml\nservices:\n  app:\n    image: ghcr.io/your-org/your-repo:latest  # Use pre-built GHCR image\n    environment:\n      - NODE_ENV=production\n    volumes:\n      - /host/path:/container/path  # Volumes work!\n    restart: unless-stopped\n    networks:\n      - coolify\n    labels:\n      - traefik.enable=true\n      - traefik.http.routers.your-app.rule=Host(`your-app.example.com`)\n      - traefik.http.routers.your-app.entrypoints=https\n      - traefik.http.routers.your-app.tls=true\n      - traefik.http.routers.your-app.tls.certresolver=letsencrypt\n      - traefik.http.services.your-app.loadbalancer.server.port=3000\n\nnetworks:\n  coolify:\n    external: true\n```\n\nKey points:\n- Use `image:` with GHCR URL (not `build:`) - GitHub Actions builds, not Coolify\n- Add Traefik labels for routing (dockercompose doesn't auto-add them)\n- Join the `coolify` external network for Traefik discovery\n- Volumes defined here actually work (unlike `custom_docker_run_options`)\n\n## Gateway Connection\n\nThe OpenClaw gateway URL is deployment-specific. Configure it explicitly:\n- URL: `OPENCLAW_GATEWAY_URL=https://openclaw.example.com`\n- Auth: Bearer token via `OPENCLAW_GATEWAY_TOKEN`\n- Public chat URL base: `OPENCLAW_GATEWAY_PUBLIC_URL=https://openclaw.example.com`\n- Agent routing: `OPENCLAW_AGENT_ID=main` by default, or set `OPENCLAW_AGENT_ID=qa` for a dedicated deployment\n- Recommendation: mint a dedicated jean-ci token/device identity instead of reusing a human operator token\n\nIf you're running behind a reverse proxy or bridge, point `OPENCLAW_GATEWAY_URL` at that endpoint.\n\nFeature sessions use `OPENCLAW_GATEWAY_PUBLIC_URL` to build browser links like `https://openclaw.example.com/chat?session=agent%3Aqa%3A...` so users can continue the real conversation in the gateway UI.\n\n### Gateway auth recovery\n\nCurrent jean-ci uses HTTP calls to `/v1/chat/completions` or `/v1/responses`, and websocket RPC for exported gateway methods like `chat.send` / `sessions.list`.\nTrue device pairing still happens on the gateway/device-auth side.\n\nWhat jean-ci does now:\n- keeps the existing HTTP integration intact\n- parses structured gateway auth recovery details when they are returned\n- surfaces actionable guidance for cases like:\n  - `AUTH_TOKEN_MISMATCH`\n  - `AUTH_DEVICE_TOKEN_MISMATCH`\n  - `PAIRING_REQUIRED`\n\nRecommended operational flow:\n1. Give jean-ci its own stable identity/token instead of a personal token\n2. Approve or rotate that device/token on the gateway when needed\n3. Keep `OPENCLAW_GATEWAY_TOKEN` pointed at the dedicated jean-ci credential\n\nUseful commands:\n- `openclaw devices list`\n- `openclaw devices approve \u003crequestId\u003e`\n- `openclaw devices rotate --device \u003cdeviceId\u003e --role operator --scope operator.read --scope operator.write --scope operator.admin`\n\n### Pairing required: what to do\n\nIf jean-ci logs or PR checks show `PAIRING_REQUIRED`, the gateway is rejecting the websocket connect until this jean-ci device is explicitly approved.\n\nRecommended recovery flow:\n1. Run `openclaw devices list`\n2. Find the pending jean-ci device request and note both the request id and device id\n3. Approve it with `openclaw devices approve \u003crequestId\u003e`\n4. Run `openclaw devices list` again and verify the device is now approved\n5. Re-run the failed jean-ci check/job\n\nIf approval does not fix it, rotate the credential for that device:\n- `openclaw devices rotate --device \u003cdeviceId\u003e --role operator --scope operator.read --scope operator.write --scope operator.admin`\n\nAdmin UI helpers:\n- Dashboard now shows the scopes jean-ci requests for its gateway device token.\n- You can revoke the cached stored device token from the admin dashboard.\n- After revocation, trigger any gateway action (for example from the gateway playground) and jean-ci will request a fresh token that includes `operator.admin`.\n\nOperational note:\n- jean-ci should use its own dedicated gateway identity/device, not a personal human operator token\n- if the gateway returns a specific device id in the error details, surface it directly in logs/errors to make approval faster\n\n## Using doubleagent for local GitHub testing\n\nRepo: \u003chttps://github.com/islo-labs/doubleagent\u003e\n\n`doubleagent` can be useful for **partial local testing** of jean-ci, especially when you want fast PR/webhook iteration without hitting the real GitHub API.\n\nWhat it is good for:\n- fake repositories\n- fake pull requests\n- fake webhook delivery\n- fast local iteration without rate limits or cleanup pain\n\nWhat it is **not** enough for today:\n- GitHub App installation/auth flows\n- GitHub Checks / check runs\n- GitHub Deployments API flows\n- package / registry-driven deployment events\n\nPractical recommendation:\n- use `doubleagent` for PR + webhook testing\n- use real GitHub for checks, deployments, and GitHub App behavior\n\nIn other words: it is a good **partial test harness**, not a full replacement for end-to-end jean-ci integration testing.\n\n## Advanced: Browser-Based E2E Tests\n\njean-ci can run natural language E2E tests using OpenClaw's browser capabilities.\n\n### Enable OpenResponses API\n\nSet the environment variable to use the full agent codepath:\n\n```bash\nOPENCLAW_USE_RESPONSES=true\n```\n\nThis switches the HTTP path from `/v1/chat/completions` (LLM only) to `/v1/responses` (full agent with tools).\nIt does not imply a websocket RPC method named `responses.create`.\n\n**Requires:** The Gateway must have responses endpoint enabled:\n```json\n{\n  \"gateway\": {\n    \"http\": {\n      \"endpoints\": {\n        \"responses\": { \"enabled\": true }\n      }\n    }\n  }\n}\n```\n\n### Writing E2E Tests\n\nCreate `.jean-ci/pr-checks/e2e-*.md` files with natural language test instructions:\n\n```markdown\n## Test: Login Flow\n\n1. Open https://your-app.com in the browser\n2. Click \"Sign In\"\n3. Enter test@example.com as email\n4. Submit the form\n5. Verify \"Welcome back\" appears on the dashboard\n\nVERDICT: PASS if all steps succeed, FAIL otherwise.\n```\n\nThe agent will:\n- Use browser automation to execute each step\n- Take screenshots on failure\n- Report PASS/FAIL based on results\n\n### Using Playwright CLI\n\nFor reliable browser automation, instruct the agent to use Playwright CLI via exec:\n\n```markdown\n## Test: Verify Dashboard Loads\n\nUse the `exec` tool to run Playwright commands:\n\n```bash\nnpx playwright screenshot --browser chromium 'https://your-app.com' /tmp/screenshot.png\n```\n\n### Steps:\n1. Run the Playwright screenshot command above\n2. Verify the command succeeds (exit code 0)\n3. Optionally fetch the page with web_fetch to verify content\n\nVERDICT: PASS if screenshot succeeds, FAIL otherwise.\n```\n\nThis approach:\n- Works reliably without browser service configuration\n- Uses Playwright's built-in Chromium\n- Supports screenshots, PDFs, and page content verification\n\n## Post-Deployment Smoke Tests\n\njean-ci can run smoke tests automatically after successful Coolify deployments.\n\n### Setup\n\nAdd markdown files to `.jean-ci/smoke-tests/` in your repository:\n\n```\n.jean-ci/\n  pr-checks/         # Run on PRs (before merge)\n  smoke-tests/       # Run after deployment\n    health.md        # Basic health check\n    e2e-login.md     # Login flow test\n    api-check.md     # API endpoint verification\n```\n\n### How It Works\n\n```\nCoolify Deploy Success → Webhook → jean-ci:\n  1. Fetch .jean-ci/smoke-tests/*.md\n  2. Run each test via OpenResponses API\n  3. Report results as GitHub Check on commit\n```\n\n### Example Smoke Test\n\n`.jean-ci/smoke-tests/health.md`:\n```markdown\n## Post-Deployment Health Check\n\nVerify the deployed application is healthy.\n\n### Steps:\n1. Fetch {{APP_URL}}/api/health with web_fetch\n2. Verify response contains \"ok\" or \"healthy\"\n3. Use exec to run: `curl -s {{APP_URL}}/api/health | jq .status`\n\n### Verdict:\n- VERDICT: PASS if health endpoint returns success\n- VERDICT: FAIL if endpoint is unreachable or returns error\n```\n\n### Available Variables\n\nVariables in smoke test prompts are automatically replaced:\n\n| Variable | Description |\n|----------|-------------|\n| `{{APP_URL}}` | Deployed application URL |\n| `{{OWNER}}` | Repository owner |\n| `{{REPO}}` | Repository name |\n| `{{SHA}}` | Deployed commit SHA |\n\n### Viewing Results\n\nSmoke test results appear as GitHub Checks on the deployed commit:\n- `jean-ci / smoke-test: health` ✅\n- `jean-ci / smoke-test: e2e-login` ❌\n\nClick through to see detailed output and failure reasons\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftelegraphic-dev%2Fjean-ci","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftelegraphic-dev%2Fjean-ci","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftelegraphic-dev%2Fjean-ci/lists"}