claw-tap 0.0.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claw_tap-0.0.5/.agents/skills/codex-e2e-test/SKILL.md +128 -0
- claw_tap-0.0.5/.agents/skills/demo-video/SKILL.md +86 -0
- claw_tap-0.0.5/.agents/skills/e2e-test/SKILL.md +40 -0
- claw_tap-0.0.5/.agents/skills/js-in-html-testing/SKILL.md +120 -0
- claw_tap-0.0.5/.agents/skills/legibility-check/SKILL.md +45 -0
- claw_tap-0.0.5/.agents/skills/playwright-screen-recording/SKILL.md +92 -0
- claw_tap-0.0.5/.agents/skills/pr-preflight/SKILL.md +79 -0
- claw_tap-0.0.5/.agents/skills/push-release/SKILL.md +46 -0
- claw_tap-0.0.5/.agents/skills/real-e2e-test/SKILL.md +83 -0
- claw_tap-0.0.5/.agents/skills/screenshot-validation/SKILL.md +76 -0
- claw_tap-0.0.5/.agents/skills/translate-i18n/SKILL.md +69 -0
- claw_tap-0.0.5/.all-contributorsrc +72 -0
- claw_tap-0.0.5/.claude/CLAUDE.md +12 -0
- claw_tap-0.0.5/.dockerignore +13 -0
- claw_tap-0.0.5/.githooks/pre-commit +12 -0
- claw_tap-0.0.5/.github/ISSUE_TEMPLATE/bug_report.yml +86 -0
- claw_tap-0.0.5/.github/ISSUE_TEMPLATE/config.yml +5 -0
- claw_tap-0.0.5/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
- claw_tap-0.0.5/.github/ISSUE_TEMPLATE.md +24 -0
- claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/chore.md +30 -0
- claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/docs.md +20 -0
- claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/feature.md +35 -0
- claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/fix.md +37 -0
- claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/plan.md +20 -0
- claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/refactor.md +30 -0
- claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/test.md +33 -0
- claw_tap-0.0.5/.github/pull_request_template.md +22 -0
- claw_tap-0.0.5/.github/workflows/auto-release.yml +94 -0
- claw_tap-0.0.5/.github/workflows/ci.yml +54 -0
- claw_tap-0.0.5/.github/workflows/legibility.yml +18 -0
- claw_tap-0.0.5/.github/workflows/publish.yml +145 -0
- claw_tap-0.0.5/.gitignore +98 -0
- claw_tap-0.0.5/.pre-commit-config.yaml +33 -0
- claw_tap-0.0.5/.python-version +1 -0
- claw_tap-0.0.5/AGENTS.md +85 -0
- claw_tap-0.0.5/CHANGELOG.md +375 -0
- claw_tap-0.0.5/CODE_OF_CONDUCT.md +35 -0
- claw_tap-0.0.5/CONTRIBUTING.md +68 -0
- claw_tap-0.0.5/Dockerfile +19 -0
- claw_tap-0.0.5/LICENSE +21 -0
- claw_tap-0.0.5/PKG-INFO +259 -0
- claw_tap-0.0.5/README.md +226 -0
- claw_tap-0.0.5/README_zh.md +220 -0
- claw_tap-0.0.5/SECURITY.md +40 -0
- claw_tap-0.0.5/claude_tap/__init__.py +47 -0
- claw_tap-0.0.5/claude_tap/__main__.py +36 -0
- claw_tap-0.0.5/claude_tap/certs.py +212 -0
- claw_tap-0.0.5/claude_tap/claw_session.py +42 -0
- claw_tap-0.0.5/claude_tap/cli.py +830 -0
- claw_tap-0.0.5/claude_tap/cursor_transcript.py +206 -0
- claw_tap-0.0.5/claude_tap/export.py +266 -0
- claw_tap-0.0.5/claude_tap/forward_proxy.py +822 -0
- claw_tap-0.0.5/claude_tap/live.py +308 -0
- claw_tap-0.0.5/claude_tap/proxy.py +760 -0
- claw_tap-0.0.5/claude_tap/py.typed +0 -0
- claw_tap-0.0.5/claude_tap/session_dispatcher.py +133 -0
- claw_tap-0.0.5/claude_tap/session_index.py +217 -0
- claw_tap-0.0.5/claude_tap/sse.py +243 -0
- claw_tap-0.0.5/claude_tap/trace.py +80 -0
- claw_tap-0.0.5/claude_tap/viewer.html +4154 -0
- claw_tap-0.0.5/claude_tap/viewer.py +366 -0
- claw_tap-0.0.5/claw_tap.egg-info/PKG-INFO +259 -0
- claw_tap-0.0.5/claw_tap.egg-info/SOURCES.txt +238 -0
- claw_tap-0.0.5/claw_tap.egg-info/dependency_links.txt +1 -0
- claw_tap-0.0.5/claw_tap.egg-info/entry_points.txt +2 -0
- claw_tap-0.0.5/claw_tap.egg-info/requires.txt +10 -0
- claw_tap-0.0.5/claw_tap.egg-info/top_level.txt +1 -0
- claw_tap-0.0.5/docker-compose.yml +30 -0
- claw_tap-0.0.5/docs/architecture/manifest.yaml +10 -0
- claw_tap-0.0.5/docs/architecture.png +0 -0
- claw_tap-0.0.5/docs/architecture.svg +198 -0
- claw_tap-0.0.5/docs/billing-header-diff.png +0 -0
- claw_tap-0.0.5/docs/demo.gif +0 -0
- claw_tap-0.0.5/docs/demo.mp4 +0 -0
- claw_tap-0.0.5/docs/demo_zh.gif +0 -0
- claw_tap-0.0.5/docs/demo_zh.mp4 +0 -0
- claw_tap-0.0.5/docs/deploy-compose.md +30 -0
- claw_tap-0.0.5/docs/diff-modal.png +0 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-02-25-pr1-stale-base.md +36 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-02-26-codex-sandbox-git-blocked.md +28 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-02-26-codex-sandbox-tmux-blocked.md +26 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-02-26-hardcoded-version-string.md +23 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-02-26-python313-ssl-aki-required.md +68 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-02-26-rg-not-portable.md +33 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-02-26-sigttou-suspend-on-exit.md +30 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-02-27-pr-screenshot-cache-stale.md +35 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-02-28-codex-reverse-websocket-capture-gap.md +35 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-03-03-codex-ws-timeout-fallback-evidence.md +36 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-03-03-pr-screenshot-quality-failures.md +57 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-03-03-pr22-websocket-screenshot-quality-regression.md +43 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-03-03-ws-proxy-debugging-failure.md +92 -0
- claw_tap-0.0.5/docs/error-experience/entries/2026-03-10-codex-strip-prefix-url-mismatch.md +61 -0
- claw_tap-0.0.5/docs/evidence/PR44_VERIFY_REPORT.md +72 -0
- claw_tap-0.0.5/docs/evidence/WS_VERIFY_REPORT.md +86 -0
- claw_tap-0.0.5/docs/evidence/bedrock-default-filter/sidebar-default-main-turns.png +0 -0
- claw_tap-0.0.5/docs/evidence/bedrock-eventstream-viewer/bedrock-response-viewer.png +0 -0
- claw_tap-0.0.5/docs/evidence/issue87/codex-real-e2e-viewer.png +0 -0
- claw_tap-0.0.5/docs/evidence/issue87/codex-sdk-real-e2e-viewer.png +0 -0
- claw_tap-0.0.5/docs/evidence/issue87/codex-sdk-real-ws-continuation-warning.png +0 -0
- claw_tap-0.0.5/docs/evidence/issue87/codex-sdk-real-ws-message-history.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr22/trace-viewer-summary.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr22/ws-upgrade-log.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr22-websocket-last-turn-wide.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr22-websocket-viewer-wide.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr26-test-evidence.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr44/pr44-turn2-messages.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr44/pr44-turn2-response.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr63/pr63-collapsed.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr63/pr63-expanded.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr73/README.md +134 -0
- claw_tap-0.0.5/docs/evidence/pr73/pr73-cxx-forward-websocket-fixed-output.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr73/pr73-cxx-forward-websocket.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr73/pr73-direct-forward-websocket-fixed-output.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr73/pr73-direct-forward-websocket.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr74/README.md +69 -0
- claw_tap-0.0.5/docs/evidence/pr74/pr74-cxx-first-message-no-reconnect.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr75/README.md +57 -0
- claw_tap-0.0.5/docs/evidence/pr75/pr75-codex-request-context-label.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr79/export-html-viewer.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr94/dashboard-real-traces.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr99/viewer-real-codex-tool-name-fallback.png +0 -0
- claw_tap-0.0.5/docs/evidence/pr99/viewer-tool-name-fallback.png +0 -0
- claw_tap-0.0.5/docs/gen_architecture.py +65 -0
- claw_tap-0.0.5/docs/good-experience/entries/2026-02-25-mock-e2e-pattern.md +48 -0
- claw_tap-0.0.5/docs/good-experience/entries/2026-02-25-proxy-e2e-ci-hardening.md +43 -0
- claw_tap-0.0.5/docs/good-experience/entries/2026-02-26-tmux-e2e-success.md +40 -0
- claw_tap-0.0.5/docs/good-experience/entries/2026-02-27-viewer-sticky-validation-flow.md +25 -0
- claw_tap-0.0.5/docs/guides/OPENCLAW_README.md +154 -0
- claw_tap-0.0.5/docs/guides/engineering-practices.md +130 -0
- claw_tap-0.0.5/docs/guides/new-client-integration-playbook.md +190 -0
- claw_tap-0.0.5/docs/guides/viewer-ux-rationale.md +42 -0
- claw_tap-0.0.5/docs/images/viewer-brand-refresh-real-e2e-2turn-v1.png +0 -0
- claw_tap-0.0.5/docs/images/viewer-brand-refresh-real-e2e-tmux-v1.png +0 -0
- claw_tap-0.0.5/docs/images/viewer-brand-refresh-real-v1.png +0 -0
- claw_tap-0.0.5/docs/images/viewer-brand-refresh-v1.png +0 -0
- claw_tap-0.0.5/docs/images/viewer-sticky-actionbar-scrolled.png +0 -0
- claw_tap-0.0.5/docs/images/viewer-sticky-actionbar-top.png +0 -0
- claw_tap-0.0.5/docs/images/viewer-sticky-after-v2.png +0 -0
- claw_tap-0.0.5/docs/images/viewer-sticky-after.png +0 -0
- claw_tap-0.0.5/docs/images/viewer-sticky-before-v2.png +0 -0
- claw_tap-0.0.5/docs/images/viewer-sticky-before.png +0 -0
- claw_tap-0.0.5/docs/plans/2026-02-27-codex-support-handoff.md +243 -0
- claw_tap-0.0.5/docs/plans/2026-02-27-codex-support-plan.md +58 -0
- claw_tap-0.0.5/docs/plans/2026-02-27-viewer-sticky-e2e-workflow.md +51 -0
- claw_tap-0.0.5/docs/plans/2026-02-28-codex-websocket-support.md +59 -0
- claw_tap-0.0.5/docs/recordings/01_error_banner_and_sidebar.png +0 -0
- claw_tap-0.0.5/docs/recordings/02_sidebar_error_styling.png +0 -0
- claw_tap-0.0.5/docs/recordings/03_copy_button_fallback_success.png +0 -0
- claw_tap-0.0.5/docs/recordings/codex-demo.cast +25 -0
- claw_tap-0.0.5/docs/recordings/codex-demo.gif +0 -0
- claw_tap-0.0.5/docs/recordings/diff-scroll-bottom.png +0 -0
- claw_tap-0.0.5/docs/recordings/diff-scroll-top.png +0 -0
- claw_tap-0.0.5/docs/recordings/record_viewer.py +170 -0
- claw_tap-0.0.5/docs/recordings/search-01-open.png +0 -0
- claw_tap-0.0.5/docs/recordings/search-02-highlight.png +0 -0
- claw_tap-0.0.5/docs/recordings/search-03-navigate.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-01-turn1-overview.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-02-tools-sse-expanded.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-03-request-json-scrolled.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-04-turn5.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-05-diff.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-06-curl.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-07-turn10.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-08-dark-mode.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-09-sidebar-scrolled.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-10-last-turn.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-11-final-wide.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-codex-01-overview.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-codex-02-messages.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-codex-02-system-prompt.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-codex-03-messages.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-codex-03-scrolled.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-codex-04-response.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-codex-04-turn3.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-codex-05-diff.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-codex-05-tokens.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-codex-06-diff.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-codex-08-tools.png +0 -0
- claw_tap-0.0.5/docs/recordings/viewer-demo.mp4 +0 -0
- claw_tap-0.0.5/docs/standards/README.md +20 -0
- claw_tap-0.0.5/docs/standards/coding-and-runtime.md +35 -0
- claw_tap-0.0.5/docs/standards/debugging-standards.md +104 -0
- claw_tap-0.0.5/docs/standards/e2e-and-evidence.md +70 -0
- claw_tap-0.0.5/docs/standards/hard-rules.md +18 -0
- claw_tap-0.0.5/docs/standards/screenshot-standards.md +41 -0
- claw_tap-0.0.5/docs/standards/validation-and-gates.md +35 -0
- claw_tap-0.0.5/docs/standards/workflow-and-review.md +48 -0
- claw_tap-0.0.5/docs/support-matrix.md +117 -0
- claw_tap-0.0.5/docs/viewer-dark.png +0 -0
- claw_tap-0.0.5/docs/viewer-light.png +0 -0
- claw_tap-0.0.5/docs/viewer-zh.png +0 -0
- claw_tap-0.0.5/pyproject.toml +100 -0
- claw_tap-0.0.5/scripts/README.md +62 -0
- claw_tap-0.0.5/scripts/check_changelog.py +90 -0
- claw_tap-0.0.5/scripts/check_legibility.py +257 -0
- claw_tap-0.0.5/scripts/check_pr.sh +265 -0
- claw_tap-0.0.5/scripts/check_screenshots.py +370 -0
- claw_tap-0.0.5/scripts/check_screenshots.sh +139 -0
- claw_tap-0.0.5/scripts/run_real_e2e.sh +117 -0
- claw_tap-0.0.5/scripts/run_real_e2e_tmux.sh +158 -0
- claw_tap-0.0.5/scripts/translate_i18n.py +407 -0
- claw_tap-0.0.5/scripts/update_changelog.py +101 -0
- claw_tap-0.0.5/scripts/verify_screenshots.py +61 -0
- claw_tap-0.0.5/setup.cfg +4 -0
- claw_tap-0.0.5/tests/__init__.py +0 -0
- claw_tap-0.0.5/tests/conftest.py +73 -0
- claw_tap-0.0.5/tests/e2e/README.md +91 -0
- claw_tap-0.0.5/tests/e2e/__init__.py +1 -0
- claw_tap-0.0.5/tests/e2e/conftest.py +82 -0
- claw_tap-0.0.5/tests/e2e/test_real_proxy.py +243 -0
- claw_tap-0.0.5/tests/fixtures/codex_ws_multi_response_trace.jsonl +1 -0
- claw_tap-0.0.5/tests/fixtures/openai_responses_trace.jsonl +1 -0
- claw_tap-0.0.5/tests/tap_cli.py +45 -0
- claw_tap-0.0.5/tests/test_bedrock_viewer.py +192 -0
- claw_tap-0.0.5/tests/test_chat_completions_sse.py +215 -0
- claw_tap-0.0.5/tests/test_check_changelog.py +49 -0
- claw_tap-0.0.5/tests/test_check_legibility.py +178 -0
- claw_tap-0.0.5/tests/test_check_pr_script.py +151 -0
- claw_tap-0.0.5/tests/test_check_screenshots.py +98 -0
- claw_tap-0.0.5/tests/test_claw_session_and_dispatcher.py +93 -0
- claw_tap-0.0.5/tests/test_codex_launch.py +127 -0
- claw_tap-0.0.5/tests/test_cursor_launch.py +174 -0
- claw_tap-0.0.5/tests/test_diff_matching.py +446 -0
- claw_tap-0.0.5/tests/test_e2e.py +2696 -0
- claw_tap-0.0.5/tests/test_export.py +139 -0
- claw_tap-0.0.5/tests/test_nav_browser.py +311 -0
- claw_tap-0.0.5/tests/test_opencode_launch.py +102 -0
- claw_tap-0.0.5/tests/test_opencode_viewer.py +69 -0
- claw_tap-0.0.5/tests/test_path_allowlist.py +52 -0
- claw_tap-0.0.5/tests/test_perf_viewer.py +336 -0
- claw_tap-0.0.5/tests/test_responses_browser.py +334 -0
- claw_tap-0.0.5/tests/test_responses_support.py +127 -0
- claw_tap-0.0.5/tests/test_search_browser.py +458 -0
- claw_tap-0.0.5/tests/test_translate_i18n.py +179 -0
- claw_tap-0.0.5/tests/test_update_changelog.py +54 -0
- claw_tap-0.0.5/tests/test_viewer_non_dict_body.py +203 -0
- claw_tap-0.0.5/tests/test_windows_compat.py +99 -0
- claw_tap-0.0.5/tests/test_ws_proxy.py +504 -0
- claw_tap-0.0.5/uv.lock +1059 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codex-e2e-test
|
|
3
|
+
description: Run real E2E tests against Codex CLI (OpenAI Responses API) through claude-tap proxy
|
|
4
|
+
tags: testing, e2e, codex, responses-api
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Codex E2E Test Skill
|
|
8
|
+
|
|
9
|
+
Run real end-to-end tests that start `claude-tap` from local source, connect to
|
|
10
|
+
the real Codex CLI via OAuth, and verify Responses API trace output.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
- `codex` CLI installed (`npm install -g @openai/codex`) and authenticated via OAuth
|
|
15
|
+
- Python dev dependencies: `uv sync --extra dev`
|
|
16
|
+
- Playwright installed: `uv run playwright install chromium`
|
|
17
|
+
|
|
18
|
+
Verify OAuth works:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
codex exec "say hello" --dangerously-bypass-approvals-and-sandbox
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If it fails with token errors, re-authenticate:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
codex auth login
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Key Difference from Claude E2E
|
|
31
|
+
|
|
32
|
+
Codex uses the **OpenAI Responses API** (`/v1/responses`) instead of Anthropic Messages API.
|
|
33
|
+
With OAuth authentication, the upstream is `https://chatgpt.com/backend-api/codex`,
|
|
34
|
+
**not** `https://api.openai.com`.
|
|
35
|
+
|
|
36
|
+
The proxy must be told the correct target with `--tap-target`.
|
|
37
|
+
|
|
38
|
+
## Run a Real Codex E2E Trace
|
|
39
|
+
|
|
40
|
+
### Simple (single tool call)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
claude-tap --tap-client codex \
|
|
44
|
+
--tap-target https://chatgpt.com/backend-api/codex \
|
|
45
|
+
--tap-output-dir /tmp/codex-e2e \
|
|
46
|
+
--tap-no-open --tap-no-update-check \
|
|
47
|
+
-- exec "say hello" \
|
|
48
|
+
--dangerously-bypass-approvals-and-sandbox
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Multi-call (triggers multiple API requests)
|
|
52
|
+
|
|
53
|
+
Use a task that requires shell tool use — this forces the agent to make multiple
|
|
54
|
+
Responses API calls (models lookup + actual responses):
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
claude-tap --tap-client codex \
|
|
58
|
+
--tap-target https://chatgpt.com/backend-api/codex \
|
|
59
|
+
--tap-output-dir /tmp/codex-e2e \
|
|
60
|
+
--tap-no-open --tap-no-update-check \
|
|
61
|
+
-- exec "Read pyproject.toml and tell me the project name and version" \
|
|
62
|
+
--dangerously-bypass-approvals-and-sandbox
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Expected: 4+ API calls (2x `GET /v1/models` + 2x `POST /v1/responses`).
|
|
66
|
+
|
|
67
|
+
## Taking Viewer Screenshots with Playwright
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from playwright.sync_api import sync_playwright
|
|
71
|
+
import time, glob
|
|
72
|
+
|
|
73
|
+
html = glob.glob("/tmp/codex-e2e/trace_*.html")[-1]
|
|
74
|
+
|
|
75
|
+
with sync_playwright() as p:
|
|
76
|
+
browser = p.chromium.launch()
|
|
77
|
+
page = browser.new_page(viewport={"width": 1440, "height": 1000})
|
|
78
|
+
page.goto(f"file://{html}")
|
|
79
|
+
page.wait_for_load_state("networkidle")
|
|
80
|
+
time.sleep(1)
|
|
81
|
+
|
|
82
|
+
# Select a Responses call (data-idx matches trace line index)
|
|
83
|
+
page.click('.sidebar-item[data-idx="1"]')
|
|
84
|
+
time.sleep(0.5)
|
|
85
|
+
|
|
86
|
+
# Collapse System Prompt, keep Messages open
|
|
87
|
+
page.evaluate("""() => {
|
|
88
|
+
const h = document.querySelectorAll('.section-header')[1];
|
|
89
|
+
const next = h.nextElementSibling;
|
|
90
|
+
if (next && getComputedStyle(next).display !== 'none') h.click();
|
|
91
|
+
}""")
|
|
92
|
+
|
|
93
|
+
# Scroll to Messages section
|
|
94
|
+
page.evaluate("""() => {
|
|
95
|
+
document.querySelectorAll('.section-header')[2]
|
|
96
|
+
.scrollIntoView({behavior: 'instant', block: 'start'});
|
|
97
|
+
}""")
|
|
98
|
+
time.sleep(0.3)
|
|
99
|
+
page.screenshot(path="/tmp/codex-e2e/messages.png")
|
|
100
|
+
|
|
101
|
+
browser.close()
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Verification Checklist
|
|
105
|
+
|
|
106
|
+
- [ ] Trace `.jsonl` has ≥2 `POST /v1/responses` entries
|
|
107
|
+
- [ ] Response status is 200 (not 401/502)
|
|
108
|
+
- [ ] Token counts are non-zero in Responses calls
|
|
109
|
+
- [ ] HTML viewer is generated (`trace_*.html`)
|
|
110
|
+
- [ ] Sidebar shows multiple calls with model name and token counts
|
|
111
|
+
- [ ] Messages section shows `user` message text (verifies #41 fix)
|
|
112
|
+
- [ ] Response section shows assistant reply (verifies #40 fix)
|
|
113
|
+
|
|
114
|
+
## Troubleshooting
|
|
115
|
+
|
|
116
|
+
| Symptom | Cause | Fix |
|
|
117
|
+
|---------|-------|-----|
|
|
118
|
+
| WebSocket 502 then HTTP 401 | Default target `api.openai.com` rejects ChatGPT OAuth tokens | Use `--tap-target https://chatgpt.com/backend-api/codex` |
|
|
119
|
+
| `Missing scopes: api.responses.write` | API key lacks Responses API access | Use OAuth (`codex auth login`) instead of `OPENAI_API_KEY` |
|
|
120
|
+
| Only 1 API call | Simple prompt completed in one round | Use a task requiring tool use (file reads, shell commands) |
|
|
121
|
+
| `OPENAI_BASE_URL is deprecated` warning | Codex v0.115+ prefers config.toml | Harmless — proxy still works via env var |
|
|
122
|
+
|
|
123
|
+
## Notes
|
|
124
|
+
|
|
125
|
+
- Codex with OAuth uses WebSocket first, then falls back to HTTP/SSE when proxied.
|
|
126
|
+
The fallback is transparent — traces capture the HTTP/SSE path correctly.
|
|
127
|
+
- Each `codex exec` session also calls `GET /v1/models` for model discovery.
|
|
128
|
+
- The `--dangerously-bypass-approvals-and-sandbox` flag is required for non-interactive exec.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: demo-video
|
|
3
|
+
description: Generate demo assets (GIF/MP4) from real tmux E2E runs and viewer screenshots using asciinema and Playwright
|
|
4
|
+
user_invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Skill: Demo Video Generation
|
|
8
|
+
|
|
9
|
+
Generate demo assets from a real tmux E2E run and viewer screenshots.
|
|
10
|
+
|
|
11
|
+
## Proven Workflow
|
|
12
|
+
|
|
13
|
+
### 1) Record terminal session in tmux with asciinema
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
tmux new-session -d -s demo -x 160 -y 46
|
|
17
|
+
tmux send-keys -t demo "asciinema rec /tmp/claude-tap-recordings/demo.cast" Enter
|
|
18
|
+
tmux send-keys -t demo "cd /path/to/claude-tap && scripts/run_real_e2e_tmux.sh" Enter
|
|
19
|
+
# ... wait until run finishes ...
|
|
20
|
+
tmux send-keys -t demo "exit" Enter
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Notes:
|
|
24
|
+
|
|
25
|
+
- `Enter` is the submit key for Claude Code TUI in tmux.
|
|
26
|
+
- Use tool-triggering first prompt text so trace includes `tool_use`.
|
|
27
|
+
|
|
28
|
+
### 2) Convert `.cast` to GIF with `agg`
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
agg /tmp/claude-tap-recordings/demo.cast /tmp/claude-tap-recordings/demo.gif
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 3) Convert GIF to MP4 with ffmpeg
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
ffmpeg -y -i /tmp/claude-tap-recordings/demo.gif -movflags +faststart -pix_fmt yuv420p docs/demo.mp4
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Browser Screenshots (HTML Viewer)
|
|
41
|
+
|
|
42
|
+
Use Playwright CDP to attach to a running Chrome/Chromium instance on port `9222`.
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
browser = playwright.chromium.connect_over_cdp("http://127.0.0.1:9222")
|
|
46
|
+
page = browser.contexts[0].pages[0]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Reliable UI interactions
|
|
50
|
+
|
|
51
|
+
- Click entries by visible text content, for example:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
page.query_selector('text="轮次 22"').click()
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- Open diff view by clicking button text:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
page.query_selector('text="对比上次"').click()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- For SPA/overflow layouts, scroll actual scrollable containers (not `window`):
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
page.evaluate("""
|
|
67
|
+
() => {
|
|
68
|
+
for (const el of document.querySelectorAll('*')) {
|
|
69
|
+
if (el.scrollHeight > el.clientHeight) {
|
|
70
|
+
el.scrollTop += 300;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
""")
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Output Targets
|
|
78
|
+
|
|
79
|
+
- `docs/demo.gif`
|
|
80
|
+
- `docs/demo.mp4`
|
|
81
|
+
- Optional localized variants (`docs/demo_zh.gif`, `docs/demo_zh.mp4`)
|
|
82
|
+
|
|
83
|
+
## Avoid
|
|
84
|
+
|
|
85
|
+
- Do not reference non-existent scripts such as `cast_to_gif_ultra.py` or `make_final_demo_v2.py`.
|
|
86
|
+
- Do not hardcode selectors like `.detail` unless verified in the current viewer build.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: e2e-test
|
|
3
|
+
description: Run claude-tap end-to-end tests with pytest
|
|
4
|
+
user_invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# claude-tap E2E Test
|
|
8
|
+
|
|
9
|
+
Run this skill after modifying core logic in claude-tap, especially:
|
|
10
|
+
- Proxy handler / SSE reassembly (`__init__.py`)
|
|
11
|
+
- TraceWriter (JSONL writing, flush behavior)
|
|
12
|
+
- HTML viewer generation (`viewer.html`, `_generate_html_viewer`)
|
|
13
|
+
- LiveViewerServer (SSE streaming)
|
|
14
|
+
- Signal handling / graceful shutdown
|
|
15
|
+
- Smart update check / trace cleanup
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
1. Run the full test suite:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv run pytest tests/test_e2e.py -v --timeout=120
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or run a single test:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv run pytest tests/test_e2e.py::test_e2e -v # Full E2E pipeline
|
|
29
|
+
uv run pytest tests/test_e2e.py::test_trace_cleanup -v # Trace cleanup
|
|
30
|
+
uv run pytest tests/test_e2e.py::test_version_check_with_fake_pypi -v # Update check
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
2. Read the output. Each test prints `PASSED` or `FAILED`.
|
|
34
|
+
|
|
35
|
+
3. If tests fail, check:
|
|
36
|
+
- **test_e2e fails**: Core proxy pipeline issue. Check `proxy_handler`, `_handle_streaming`, `TraceWriter.write`.
|
|
37
|
+
- **test_trace_cleanup fails**: Session retention issue. Check `_cleanup_traces`, `SessionIndex.delete_oldest_sessions`, and `sessions/` layout.
|
|
38
|
+
- **test_version_check_* fails**: PyPI check logic. Check `_check_pypi_version`, `CLAUDE_TAP_PYPI_URL` env var.
|
|
39
|
+
- **test_live_viewer_* fails**: Viewer HTML issues. Check `viewer.html` for `preserveDetail` chain, `updateNavButtons`.
|
|
40
|
+
- **Timeout**: May be a network/port issue, not a claude-tap bug.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: js-in-html-testing
|
|
3
|
+
description: Test JS logic embedded in HTML using two-layer strategy - Python unit tests + Playwright browser integration tests
|
|
4
|
+
user_invocable: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# JS-in-HTML Two-Layer Testing Strategy
|
|
8
|
+
|
|
9
|
+
For JavaScript logic embedded in HTML files (e.g., diff navigation in viewer.html), use a two-layer testing approach.
|
|
10
|
+
|
|
11
|
+
## Layer 1: Python Unit Tests (fast algorithm verification)
|
|
12
|
+
|
|
13
|
+
Replicate core JS algorithms in Python and verify correctness via pytest.
|
|
14
|
+
|
|
15
|
+
Best for: pure computation logic, state decisions, matching algorithms — anything that doesn't depend on the DOM.
|
|
16
|
+
|
|
17
|
+
**Example**: `tests/test_diff_matching.py`
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
# Replicate JS findPrevSameModel / findNextSameModel
|
|
21
|
+
def find_diff_parent_by_prefix(entries, idx):
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
def find_next_by_prefix(entries, idx):
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
# Replicate JS updateNavButtons state computation
|
|
28
|
+
def compute_nav_button_states(entries, cur_idx):
|
|
29
|
+
prev_idx = find_diff_parent_by_prefix(entries, cur_idx)
|
|
30
|
+
...
|
|
31
|
+
return (prev_enabled, next_enabled)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Advantages: fast (0.02s), no browser dependency, integrates with existing pytest setup.
|
|
35
|
+
|
|
36
|
+
## Layer 2: Playwright Browser Integration Tests (DOM interaction verification)
|
|
37
|
+
|
|
38
|
+
Generate HTML with test data embedded, open it in real Chromium via Playwright, and verify DOM state and user interactions.
|
|
39
|
+
|
|
40
|
+
Best for: button disabled states, overlay show/hide, keyboard events, click navigation, etc.
|
|
41
|
+
|
|
42
|
+
**Example**: `tests/test_nav_browser.py`
|
|
43
|
+
|
|
44
|
+
### Building test HTML
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
def _build_test_html():
|
|
48
|
+
template = Path("claude_tap/viewer.html").read_text()
|
|
49
|
+
records = [json.dumps(e) for e in TEST_ENTRIES]
|
|
50
|
+
data_js = "const EMBEDDED_TRACE_DATA = [\n" + ",\n".join(records) + "\n];\n"
|
|
51
|
+
# Inject data into template
|
|
52
|
+
return template.replace(
|
|
53
|
+
"<script>\nconst $ = s =>",
|
|
54
|
+
f"<script>\n{data_js}</script>\n<script>\nconst $ = s =>",
|
|
55
|
+
1,
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Playwright fixture
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
@pytest.fixture(scope="module")
|
|
63
|
+
def browser_page(html_file):
|
|
64
|
+
from playwright.sync_api import sync_playwright
|
|
65
|
+
pw = sync_playwright().start()
|
|
66
|
+
browser = pw.chromium.launch(headless=True)
|
|
67
|
+
page = browser.new_page()
|
|
68
|
+
page.goto(f"file://{html_file}")
|
|
69
|
+
page.wait_for_selector(".sidebar-item", timeout=5000)
|
|
70
|
+
yield page
|
|
71
|
+
browser.close()
|
|
72
|
+
pw.stop()
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Verifying DOM state
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
def _get_nav_state(page):
|
|
79
|
+
return page.evaluate("""() => {
|
|
80
|
+
const overlay = document.querySelector('.diff-overlay');
|
|
81
|
+
if (!overlay) return { overlayExists: false };
|
|
82
|
+
return {
|
|
83
|
+
overlayExists: true,
|
|
84
|
+
prevDisabled: overlay.querySelector('.diff-nav-prev')?.disabled,
|
|
85
|
+
nextDisabled: overlay.querySelector('.diff-nav-next')?.disabled,
|
|
86
|
+
title: overlay.querySelector('.diff-title')?.textContent,
|
|
87
|
+
};
|
|
88
|
+
}""")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Simulating user interaction
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
# Click buttons
|
|
95
|
+
page.click(".diff-nav-next")
|
|
96
|
+
# Keyboard navigation
|
|
97
|
+
page.keyboard.press("ArrowRight")
|
|
98
|
+
# Call internal JS functions
|
|
99
|
+
page.evaluate("selectEntry(2)")
|
|
100
|
+
page.evaluate("showDiff()")
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Notes
|
|
104
|
+
|
|
105
|
+
- Trace data may contain `</script>` text (e.g., when Claude discusses code), which breaks `<script>` block parsing. Escape it: `line.replace("</script>", '</scr" + "ipt>')`
|
|
106
|
+
- Test data must match the viewer's expected JSONL format (including `turn`, `duration_ms`, `request_id`, `request.path`, etc.)
|
|
107
|
+
- Playwright requires `uv pip install playwright`
|
|
108
|
+
|
|
109
|
+
## Running
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Fast unit tests
|
|
113
|
+
uv run pytest tests/test_diff_matching.py -v
|
|
114
|
+
|
|
115
|
+
# Browser integration tests
|
|
116
|
+
uv run pytest tests/test_nav_browser.py -v
|
|
117
|
+
|
|
118
|
+
# All (excluding slow e2e)
|
|
119
|
+
uv run pytest tests/ --ignore=tests/test_e2e.py -v
|
|
120
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: legibility-check
|
|
3
|
+
description: Validate docs structure, standards freshness, manifest paths, and plan state. Run this after modifying any file under docs/standards/, docs/plans/, docs/architecture/, or AGENTS.md — it catches stale metadata, broken manifest paths, and plan state drift before CI does.
|
|
4
|
+
user_invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Legibility Check
|
|
8
|
+
|
|
9
|
+
Run deterministic legibility checks that mirror what CI enforces via `.github/workflows/legibility.yml`. Catching these locally saves a round-trip to CI.
|
|
10
|
+
|
|
11
|
+
## What it checks
|
|
12
|
+
|
|
13
|
+
1. **Standards freshness** — every `docs/standards/*.md` must have frontmatter with `owner`, `last_reviewed` (ISO date), and `source_of_truth`. Files reviewed more than 60 days ago produce a warning.
|
|
14
|
+
2. **Architecture manifest** — every path listed in `docs/architecture/manifest.yaml` under `expected_paths:` must exist in the repo.
|
|
15
|
+
3. **Plan state drift** — every `docs/plans/**/*.md` must have a `status` frontmatter field (`active`, `completed`, or `cancelled`). Completed plans must not contain unchecked `- [ ]` checkboxes (outside fenced code blocks).
|
|
16
|
+
|
|
17
|
+
## Run
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv run python scripts/check_legibility.py
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
- `--freshness-days N` — change the staleness threshold (default: 60)
|
|
25
|
+
- `--strict-freshness` — promote stale warnings to failures
|
|
26
|
+
- `--repo-root PATH` — override repo root (default: cwd)
|
|
27
|
+
|
|
28
|
+
## Fixing common failures
|
|
29
|
+
|
|
30
|
+
| Failure | Fix |
|
|
31
|
+
|---------|-----|
|
|
32
|
+
| `missing frontmatter key 'X'` | Add the missing key to the YAML frontmatter block at the top of the file |
|
|
33
|
+
| `last_reviewed must be ISO date` | Use `YYYY-MM-DD` format |
|
|
34
|
+
| `last_reviewed ... is stale` | Update `last_reviewed` to today's date after reviewing the content |
|
|
35
|
+
| `expected path missing: X` | Either create the file or remove the stale entry from `manifest.yaml` |
|
|
36
|
+
| `status must be one of [...]` | Add `status: active` (or `completed`/`cancelled`) to plan frontmatter |
|
|
37
|
+
| `completed plan still contains unchecked TODO` | Check off remaining items or change status back to `active` |
|
|
38
|
+
|
|
39
|
+
## After fixing
|
|
40
|
+
|
|
41
|
+
Re-run the check to confirm all issues are resolved before committing:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
uv run python scripts/check_legibility.py && echo "All clear"
|
|
45
|
+
```
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: playwright-screen-recording
|
|
3
|
+
description: Record browser test videos with Playwright for PR review and bug fix verification
|
|
4
|
+
user_invocable: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Playwright Screen Recording for Test Verification
|
|
8
|
+
|
|
9
|
+
Use Playwright's video recording to capture headless browser operations as .webm videos for PR review or bug fix verification.
|
|
10
|
+
|
|
11
|
+
## Core Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from playwright.sync_api import sync_playwright
|
|
15
|
+
import tempfile
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
video_dir = Path(tempfile.mkdtemp())
|
|
19
|
+
|
|
20
|
+
with sync_playwright() as pw:
|
|
21
|
+
browser = pw.chromium.launch(headless=True)
|
|
22
|
+
context = browser.new_context(
|
|
23
|
+
viewport={"width": 1400, "height": 900},
|
|
24
|
+
record_video_dir=str(video_dir),
|
|
25
|
+
record_video_size={"width": 1400, "height": 900},
|
|
26
|
+
)
|
|
27
|
+
page = context.new_page()
|
|
28
|
+
page.goto(f"file:///path/to/test.html")
|
|
29
|
+
|
|
30
|
+
# ... perform test actions, add pauses for readability ...
|
|
31
|
+
page.wait_for_timeout(800) # pause so viewers can see the current state
|
|
32
|
+
|
|
33
|
+
page.close()
|
|
34
|
+
context.close() # video is finalized after context.close()
|
|
35
|
+
browser.close()
|
|
36
|
+
|
|
37
|
+
# Retrieve the recorded video
|
|
38
|
+
videos = list(video_dir.glob("*.webm"))
|
|
39
|
+
if videos:
|
|
40
|
+
videos[0].rename("demo.webm")
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Use Cases
|
|
44
|
+
|
|
45
|
+
- **Bug fix verification**: record before/after comparisons showing button state changes, UI behavior differences
|
|
46
|
+
- **PR Review**: attach .webm video so reviewers can visually understand the change
|
|
47
|
+
- **Regression test evidence**: record critical interaction paths as visual proof of passing tests
|
|
48
|
+
|
|
49
|
+
## Recording Tips
|
|
50
|
+
|
|
51
|
+
### Add pauses between actions
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
page.click(".some-button")
|
|
55
|
+
page.wait_for_timeout(800) # let viewers see the click effect
|
|
56
|
+
|
|
57
|
+
page.keyboard.press("ArrowRight")
|
|
58
|
+
page.wait_for_timeout(600) # let viewers see the navigation result
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Combine assertions with terminal logging
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
state = get_nav_state(page)
|
|
65
|
+
print(f"[1] Title: {state['title']}")
|
|
66
|
+
print(f" prev disabled: {state['prevDisabled']} (expected: True)")
|
|
67
|
+
assert state["prevDisabled"] is True
|
|
68
|
+
print(" PASS")
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Terminal output paired with the recorded video provides dual verification.
|
|
72
|
+
|
|
73
|
+
### Prefer real data
|
|
74
|
+
|
|
75
|
+
Use existing real trace data from the project rather than synthetic data for more convincing demos:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
# Build test HTML from a real trace file
|
|
79
|
+
records = []
|
|
80
|
+
with open(".traces/trace_xxx.jsonl") as f:
|
|
81
|
+
for line in f:
|
|
82
|
+
# Escape </script> to prevent breaking the HTML script block
|
|
83
|
+
records.append(line.strip().replace("</script>", '</scr" + "ipt>'))
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Notes
|
|
87
|
+
|
|
88
|
+
- Video format is `.webm` (VP8 codec), supported by most players and browsers
|
|
89
|
+
- Each `page` produces a separate video file
|
|
90
|
+
- `record_video_size` controls video resolution — keep it consistent with `viewport`
|
|
91
|
+
- Recording works in headless mode, no display required
|
|
92
|
+
- Video files are typically a few hundred KB, suitable for attaching to PRs or chat
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pr-preflight
|
|
3
|
+
description: Full pre-PR merge-readiness check. Run this before opening or merging a pull request — it validates local gates (lint, format, tests), CI status, screenshot evidence, and PR metadata in one pass. Also useful for reviewing an existing PR's readiness.
|
|
4
|
+
user_invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# PR Preflight
|
|
8
|
+
|
|
9
|
+
One-command merge-readiness check that combines local gates, CI status, and PR policy checks. Mirrors what reviewers look for so issues are caught before review, not during.
|
|
10
|
+
|
|
11
|
+
## Check an existing PR
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
scripts/check_pr.sh <pr_number>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This runs:
|
|
18
|
+
1. **PR metadata** — fetches title, state, draft status, merge state, branch info
|
|
19
|
+
2. **CI checks** — counts pass/fail/pending GitHub Actions checks
|
|
20
|
+
3. **Local gates** — runs lint, format, and tests locally:
|
|
21
|
+
- `uv run ruff check .`
|
|
22
|
+
- `uv run ruff format --check .`
|
|
23
|
+
- `uv run pytest tests/ -x --timeout=60`
|
|
24
|
+
4. **Screenshot evidence** — scans PR body for image links (required by project policy)
|
|
25
|
+
5. **Verdict** — `READY` or `NOT_READY` with specific reasons
|
|
26
|
+
|
|
27
|
+
### Options
|
|
28
|
+
|
|
29
|
+
| Flag | Purpose |
|
|
30
|
+
|------|---------|
|
|
31
|
+
| `--repo OWNER/REPO` | Override repository (default: auto-detect via `gh`) |
|
|
32
|
+
| `--no-tests` | Skip local test gates (useful when you just want CI + metadata check) |
|
|
33
|
+
|
|
34
|
+
### Exit codes
|
|
35
|
+
|
|
36
|
+
| Code | Meaning |
|
|
37
|
+
|------|---------|
|
|
38
|
+
| 0 | All checks passed — ready to merge |
|
|
39
|
+
| 1 | Script error (missing tool, network failure) |
|
|
40
|
+
| 2 | Not ready — at least one check failed |
|
|
41
|
+
|
|
42
|
+
## Run local gates only (no PR needed)
|
|
43
|
+
|
|
44
|
+
If you haven't opened a PR yet and just want to validate locally:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv run ruff check . && uv run ruff format --check . && uv run pytest tests/ -x --timeout=60
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or use the pre-commit hook (auto-runs lint on commit):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
git config core.hooksPath .githooks
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## What blocks merge
|
|
57
|
+
|
|
58
|
+
The script reports `NOT_READY` if any of these are true:
|
|
59
|
+
- PR is not in OPEN state
|
|
60
|
+
- PR is still a draft
|
|
61
|
+
- Merge state is not CLEAN or HAS_HOOKS
|
|
62
|
+
- Any CI check is failing
|
|
63
|
+
- Any CI check is still pending
|
|
64
|
+
- Local gates (lint/format/tests) fail
|
|
65
|
+
- No screenshot evidence in PR body
|
|
66
|
+
|
|
67
|
+
## Typical workflow
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# 1. Make sure local gates pass
|
|
71
|
+
uv run ruff check . && uv run ruff format --check . && uv run pytest tests/ -x --timeout=60
|
|
72
|
+
|
|
73
|
+
# 2. Push and open PR
|
|
74
|
+
git push origin my-branch
|
|
75
|
+
gh pr create --title "feat: ..." --body "..."
|
|
76
|
+
|
|
77
|
+
# 3. Wait for CI, then run full preflight
|
|
78
|
+
scripts/check_pr.sh <pr_number>
|
|
79
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: push-release
|
|
3
|
+
description: Push to GitHub and optionally bump version to trigger PyPI release
|
|
4
|
+
user_invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Push & Release
|
|
8
|
+
|
|
9
|
+
Push code to GitHub. If the pending commits contain feature changes, bump the version number so CI auto-publishes to PyPI.
|
|
10
|
+
|
|
11
|
+
## Workflow
|
|
12
|
+
|
|
13
|
+
1. **Check working tree**: Ensure no uncommitted changes (prompt user to commit first if dirty).
|
|
14
|
+
|
|
15
|
+
2. **Determine whether a version bump is needed**:
|
|
16
|
+
- Read current version from `pyproject.toml`
|
|
17
|
+
- Run `git log origin/main..HEAD --oneline` to inspect pending commits
|
|
18
|
+
- If commits include feature changes (feat/fix/refactor, not purely docs/chore/test), a bump is needed
|
|
19
|
+
- If only docs, tests, or CI changes, skip the bump
|
|
20
|
+
|
|
21
|
+
3. **If bump is needed**:
|
|
22
|
+
- Choose bump level based on change type:
|
|
23
|
+
- **patch** (0.1.4 → 0.1.5): bug fixes, minor improvements
|
|
24
|
+
- **minor** (0.1.4 → 0.2.0): new features
|
|
25
|
+
- **major** (0.1.4 → 1.0.0): breaking changes
|
|
26
|
+
- Update the `version` field in `pyproject.toml`
|
|
27
|
+
- Update `__version__` in `claude_tap/__init__.py`
|
|
28
|
+
- `git commit --amend` to fold the version bump into the last commit (avoids extra commits)
|
|
29
|
+
|
|
30
|
+
4. **Push**:
|
|
31
|
+
```bash
|
|
32
|
+
git push origin main
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
5. **Confirm CI status**:
|
|
36
|
+
- Inform the user that CI will automatically: lint → test → auto-tag → PyPI publish
|
|
37
|
+
- Provide the GitHub Actions link: https://github.com/liaohch3/claude-tap/actions
|
|
38
|
+
|
|
39
|
+
## Important: Version Bump = PyPI Release
|
|
40
|
+
|
|
41
|
+
The CI pipeline works as follows: push to main → auto-tag (only if version changed) → PyPI publish (triggered by new tag).
|
|
42
|
+
|
|
43
|
+
**A version bump is the ONLY way to trigger a new PyPI release.** If you push without bumping the version, CI will skip tagging and nothing gets published. So whenever commits include meaningful code changes (features, fixes, improvements), you MUST bump the version before pushing.
|
|
44
|
+
|
|
45
|
+
- Version numbers in `pyproject.toml` and `claude_tap/__init__.py` must stay in sync
|
|
46
|
+
- Only skip the bump for pure docs/test/CI changes that don't affect the published package
|