Agent Skills › liaohch3/claude-tap

liaohch3/claude-tap

GitHub

用于Codex PR验证的端到端测试技能,通过claude-tap连接真实Codex CLI,捕获OpenAI Responses API轨迹并生成截图证据。适用于涉及渲染、会话逻辑或工具调用的代码变更。

11 skills 2,347

Install All Skills

npx skills add liaohch3/claude-tap --all -g -y
More Options

List skills in collection

npx skills add liaohch3/claude-tap --list

Skills in Collection (11)

用于Codex PR验证的端到端测试技能,通过claude-tap连接真实Codex CLI,捕获OpenAI Responses API轨迹并生成截图证据。适用于涉及渲染、会话逻辑或工具调用的代码变更。
修改捕获、代理、视图器渲染逻辑 变更会话或仪表板行为 调整客户端启动逻辑 涉及内容块、工具调用顺序或令牌使用 更新截图或演示资产
.agents/skills/codex-e2e-test/SKILL.md
npx skills add liaohch3/claude-tap --skill codex-e2e-test -g -y
SKILL.md
Frontmatter
{
    "name": "codex-e2e-test",
    "tags": "testing, e2e, codex, responses-api",
    "description": "Run PR-grade real Codex E2E validation through claude-tap, including resume turns, multiple tool calls, optional image input, viewer verification, and screenshot evidence."
}

Codex E2E Test Skill

Run real end-to-end validation that starts claude-tap from local source, connects to the real Codex CLI via OAuth, captures OpenAI Responses API traces, and produces viewer screenshots suitable for PR evidence.

Use this skill for every PR that changes capture, proxying, viewer rendering, session/dashboard behavior, client launch logic, trace ordering, content blocks, tools, token usage, or screenshot/demo assets. If a PR cannot run this flow, state why in the PR and cover the same risk with another real client trace.

Prerequisites

  • codex CLI installed (npm install -g @openai/codex) and authenticated via OAuth
  • Python dev dependencies: uv sync --extra dev
  • Playwright installed: uv run playwright install chromium

Verify OAuth works:

codex exec "say hello" --dangerously-bypass-approvals-and-sandbox

If it fails with token errors, re-authenticate:

codex auth login

Key Difference from Claude E2E

Codex uses the OpenAI Responses API (/v1/responses) instead of Anthropic Messages API. With OAuth authentication, the upstream is https://chatgpt.com/backend-api/codex, not https://api.openai.com.

The proxy must be told the correct target with --tap-target.

Run a Real Codex E2E Trace

Prefer the resume + multimodal flow below for PR evidence. The simple commands are only smoke tests for checking local setup.

Simple (single tool call)

claude-tap --tap-client codex \
  --tap-target https://chatgpt.com/backend-api/codex \
  --tap-output-dir /tmp/codex-e2e \
  --tap-no-open --tap-no-update-check \
  -- exec "say hello" \
  --dangerously-bypass-approvals-and-sandbox

Multi-call (triggers multiple API requests)

Use a task that requires shell tool use — this forces the agent to make multiple Responses API calls (models lookup + actual responses):

claude-tap --tap-client codex \
  --tap-target https://chatgpt.com/backend-api/codex \
  --tap-output-dir /tmp/codex-e2e \
  --tap-no-open --tap-no-update-check \
  -- exec "Read pyproject.toml and tell me the project name and version" \
  --dangerously-bypass-approvals-and-sandbox

Expected: 4+ API calls (2x GET /v1/models + 2x POST /v1/responses).

Resume + Multimodal Content-Block Trace

Use this flow for viewer changes that affect message rendering, content block boundaries, tool call ordering, images, or copy/select behavior. It creates a real Codex session, resumes it at least once, forces multiple shell tool calls per user turn, and attaches an actual image so the trace includes multimodal content. This is the default PR evidence flow.

1. Prepare an isolated workspace

mkdir -p /tmp/claude-tap-real-codex-workspace
printf 'project = "claude-tap-real-codex-e2e"\n' \
  > /tmp/claude-tap-real-codex-workspace/project.toml

Create or copy a small valid PNG into the workspace. If you need a deterministic local image, generate it with Python's standard library:

python3 - <<'PY'
from pathlib import Path
import struct
import zlib

def chunk(kind: bytes, data: bytes) -> bytes:
    return struct.pack(">I", len(data)) + kind + data + struct.pack(">I", zlib.crc32(kind + data) & 0xFFFFFFFF)

width = height = 8
rows = b"".join(b"\x00" + (b"\x2f\x80\xed\xff" * width) for _ in range(height))
png = (
    b"\x89PNG\r\n\x1a\n"
    + chunk(b"IHDR", struct.pack(">IIBBBBB", width, height, 8, 6, 0, 0, 0))
    + chunk(b"IDAT", zlib.compress(rows))
    + chunk(b"IEND", b"")
)
Path("/tmp/claude-tap-real-codex-workspace/input.png").write_bytes(png)
PY

2. Start a real Codex session through claude-tap

Run from the repository, but point Codex at the isolated workspace with -C. The prompt should explicitly require several tool calls so the viewer has enough messages, tool calls, and response blocks to inspect.

uv run claude-tap --tap-client codex \
  --tap-target https://chatgpt.com/backend-api/codex \
  --tap-output-dir /tmp/claude-tap-real-codex-traces \
  --tap-no-open --tap-no-update-check \
  -- exec -C /tmp/claude-tap-real-codex-workspace \
  --image /tmp/claude-tap-real-codex-workspace/input.png \
  --dangerously-bypass-approvals-and-sandbox \
  "Inspect this workspace and the attached image. Use shell tools to run pwd, list files, inspect project.toml, inspect input.png, then write codex_e2e_report.txt with your findings. Keep all writes inside this workspace."

3. Resume the same Codex session with another real turn

Use the session id printed by the first Codex run when possible. Avoid relying on --last on busy maintainer machines because it can resume an unrelated recent Codex session.

uv run claude-tap --tap-client codex \
  --tap-target https://chatgpt.com/backend-api/codex \
  --tap-output-dir /tmp/claude-tap-real-codex-traces \
  --tap-no-open --tap-no-update-check \
  -- exec resume <SESSION_ID_FROM_FIRST_RUN> \
  --image /tmp/claude-tap-real-codex-workspace/input.png \
  --dangerously-bypass-approvals-and-sandbox \
  "Continue the same investigation in /tmp/claude-tap-real-codex-workspace. Use shell tools to read /tmp/claude-tap-real-codex-workspace/codex_e2e_report.txt, compute the byte size of /tmp/claude-tap-real-codex-workspace/input.png, and write /tmp/claude-tap-real-codex-workspace/codex_e2e_followup.txt. Then summarize what changed since the previous turn."

4. Capture multi-position viewer screenshots

Take screenshots at multiple scroll positions, including a deeper position in the same detail pane. Store them under .agents/evidence/pr/<topic>/ and use raw.githubusercontent.com links in the PR body.

mkdir -p .agents/evidence/pr/codex-real-e2e

uv run python - <<'PY'
from pathlib import Path
from playwright.sync_api import sync_playwright

html_files = sorted(Path("/tmp/claude-tap-real-codex-traces").rglob("trace_*.html"))
if not html_files:
    raise SystemExit("No viewer HTML found in /tmp/claude-tap-real-codex-traces")
html = html_files[-1]
out_dir = Path(".agents/evidence/pr/codex-real-e2e")

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page(viewport={"width": 1760, "height": 1100}, device_scale_factor=1)
    page.goto(f"file://{html}", wait_until="domcontentloaded", timeout=10000)
    page.wait_for_selector(".sidebar-item", timeout=5000)
    page.evaluate("document.documentElement.setAttribute('data-theme', 'light')")

    # Select the last Responses call so resume context is visible.
    page.evaluate(
        """() => {
          const items = Array.from(document.querySelectorAll('.sidebar-item'));
          const responses = items.filter(item => item.textContent.includes('/v1/responses'));
          (responses.at(-1) || items.at(-1))?.click();
        }"""
    )
    page.wait_for_timeout(300)

    page.evaluate(
        """() => {
          for (const section of document.querySelectorAll('#detail .section')) {
            const title = section.querySelector('.title')?.textContent || '';
            const body = section.querySelector('.section-body');
            const header = section.querySelector('.section-header');
            if (!body || !header) continue;
            const shouldOpen = ['System Prompt', 'Messages', 'Response'].includes(title);
            const isOpen = body.classList.contains('open');
            if (shouldOpen !== isOpen) header.click();
          }
        }"""
    )
    page.wait_for_timeout(200)

    def shot(name: str, scroll_top: int) -> None:
        page.evaluate("y => { const d = document.querySelector('#detail'); if (d) d.scrollTop = y; }", scroll_top)
        page.wait_for_timeout(200)
        page.screenshot(path=str(out_dir / name), full_page=False)

    shot("codex-real-top.png", 0)
    shot("codex-real-mid.png", 700)
    shot("codex-real-deep.png", 1400)

    image_count = page.evaluate(
        """() => {
          const img = document.querySelector('#detail img');
          if (!img) return 0;
          img.scrollIntoView({ block: 'center', inline: 'nearest' });
          return document.querySelectorAll('#detail img').length;
        }"""
    )
    if image_count:
        page.wait_for_timeout(200)
        page.screenshot(path=str(out_dir / "codex-real-image.png"), full_page=False)

    browser.close()
PY

Validate screenshots:

uv run python scripts/check_screenshots.py .agents/evidence/pr/codex-real-e2e

5. Verify the trace

  • The output directory contains at least two real .jsonl traces and generated .html viewers from claude-tap.
  • The trace includes multiple POST /v1/responses entries with status 200 and non-zero token usage.
  • At least one request contains image content from the --image attachment.
  • The viewer shows Messages and Response sections with multiple content blocks separated cleanly.
  • Tool calls and tool results stay interleaved in the order they happened.
  • Copy buttons still copy the complete logical message/section text, not only one visual content block.
  • Text selection across adjacent content blocks remains possible in the browser.
  • Screenshot evidence includes at least two scroll depths for the same resumed trace, not only the top of the page.
  • If the trace contains image input, screenshot evidence includes the rendered image block or records why the client did not send image content into the API request body.

Taking Viewer Screenshots with Playwright

from playwright.sync_api import sync_playwright
import time, glob

html = glob.glob("/tmp/codex-e2e/trace_*.html")[-1]

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page(viewport={"width": 1440, "height": 1000})
    page.goto(f"file://{html}")
    page.wait_for_load_state("networkidle")
    time.sleep(1)

    # Select a Responses call (data-idx matches trace line index)
    page.click('.sidebar-item[data-idx="1"]')
    time.sleep(0.5)

    # Collapse System Prompt, keep Messages open
    page.evaluate("""() => {
        const h = document.querySelectorAll('.section-header')[1];
        const next = h.nextElementSibling;
        if (next && getComputedStyle(next).display !== 'none') h.click();
    }""")

    # Scroll to Messages section
    page.evaluate("""() => {
        document.querySelectorAll('.section-header')[2]
          .scrollIntoView({behavior: 'instant', block: 'start'});
    }""")
    time.sleep(0.3)
    page.screenshot(path="/tmp/codex-e2e/messages.png")

    browser.close()

Verification Checklist

  • Trace .jsonl has ≥2 POST /v1/responses entries
  • Response status is 200 (not 401/502)
  • Token counts are non-zero in Responses calls
  • HTML viewer is generated (trace_*.html)
  • Sidebar shows multiple calls with model name and token counts
  • Messages section shows user message text (verifies #41 fix)
  • Response section shows assistant reply (verifies #40 fix)

Troubleshooting

Symptom Cause Fix
WebSocket 502 then HTTP 401 Default target api.openai.com rejects ChatGPT OAuth tokens Use --tap-target https://chatgpt.com/backend-api/codex
Missing scopes: api.responses.write API key lacks Responses API access Use OAuth (codex auth login) instead of OPENAI_API_KEY
Only 1 API call Simple prompt completed in one round Use a task requiring tool use (file reads, shell commands)
OPENAI_BASE_URL is deprecated warning Codex v0.115+ prefers config.toml Harmless — proxy still works via env var

Notes

  • Codex with OAuth uses WebSocket first, then falls back to HTTP/SSE when proxied. The fallback is transparent — traces capture the HTTP/SSE path correctly.
  • Each codex exec session also calls GET /v1/models for model discovery.
  • The --dangerously-bypass-approvals-and-sandbox flag is required for non-interactive exec.
基于 tmux 和 asciinema 录制终端会话,经 agg 转 GIF、ffmpeg 转 MP4 生成演示视频。结合 Playwright CDP 控制浏览器截图,实现从 E2E 运行到多格式 Demo 资产的自动化生成流程。
需要生成终端操作演示视频 需要将 asciinema 记录转换为 GIF 或 MP4 需要截取 HTML 查看器界面作为演示素材
.agents/skills/demo-video/SKILL.md
npx skills add liaohch3/claude-tap --skill demo-video -g -y
SKILL.md
Frontmatter
{
    "name": "demo-video",
    "description": "Generate demo assets (GIF\/MP4) from real tmux E2E runs and viewer screenshots using asciinema and Playwright",
    "user_invocable": true
}

Skill: Demo Video Generation

Generate demo assets from a real tmux E2E run and viewer screenshots.

Proven Workflow

1) Record terminal session in tmux with asciinema

tmux new-session -d -s demo -x 160 -y 46
tmux send-keys -t demo "asciinema rec /tmp/claude-tap-recordings/demo.cast" Enter
tmux send-keys -t demo "cd /path/to/claude-tap && scripts/run_real_e2e_tmux.sh" Enter
# ... wait until run finishes ...
tmux send-keys -t demo "exit" Enter

Notes:

  • Enter is the submit key for Claude Code TUI in tmux.
  • Use tool-triggering first prompt text so trace includes tool_use.

2) Convert .cast to GIF with agg

agg /tmp/claude-tap-recordings/demo.cast /tmp/claude-tap-recordings/demo.gif

3) Convert GIF to MP4 with ffmpeg

ffmpeg -y -i /tmp/claude-tap-recordings/demo.gif -movflags +faststart -pix_fmt yuv420p .agents/recordings/demo.mp4

Browser Screenshots (HTML Viewer)

Use Playwright CDP to attach to a running Chrome/Chromium instance on port 9222.

browser = playwright.chromium.connect_over_cdp("http://127.0.0.1:9222")
page = browser.contexts[0].pages[0]

Reliable UI interactions

  • Click entries by visible text content, for example:
page.query_selector('text="轮次 22"').click()
  • Open diff view by clicking button text:
page.query_selector('text="对比上次"').click()
  • For SPA/overflow layouts, scroll actual scrollable containers (not window):
page.evaluate("""
() => {
  for (const el of document.querySelectorAll('*')) {
    if (el.scrollHeight > el.clientHeight) {
      el.scrollTop += 300;
    }
  }
}
""")

Output Targets

  • docs/demo.gif
  • .agents/recordings/demo.mp4
  • Optional localized variants (docs/demo_zh.gif, .agents/recordings/demo_zh.mp4)

Avoid

  • Do not reference non-existent scripts such as cast_to_gif_ultra.py or make_final_demo_v2.py.
  • Do not hardcode selectors like .detail unless verified in the current viewer build.
针对嵌入HTML的JS逻辑提供双层测试策略:第一层用Python复现算法进行快速单元测试,第二层用Playwright启动Chromium验证DOM状态与交互。
测试嵌入HTML的JS逻辑 需要验证DOM交互和按钮状态
.agents/skills/js-in-html-testing/SKILL.md
npx skills add liaohch3/claude-tap --skill js-in-html-testing -g -y
SKILL.md
Frontmatter
{
    "name": "js-in-html-testing",
    "description": "Test JS logic embedded in HTML using two-layer strategy - Python unit tests + Playwright browser integration tests",
    "user_invocable": false
}

JS-in-HTML Two-Layer Testing Strategy

For JavaScript logic embedded in HTML files (e.g., diff navigation in viewer.html), use a two-layer testing approach.

Layer 1: Python Unit Tests (fast algorithm verification)

Replicate core JS algorithms in Python and verify correctness via pytest.

Best for: pure computation logic, state decisions, matching algorithms — anything that doesn't depend on the DOM.

Example: tests/test_diff_matching.py

# Replicate JS findPrevSameModel / findNextSameModel
def find_diff_parent_by_prefix(entries, idx):
    ...

def find_next_by_prefix(entries, idx):
    ...

# Replicate JS updateNavButtons state computation
def compute_nav_button_states(entries, cur_idx):
    prev_idx = find_diff_parent_by_prefix(entries, cur_idx)
    ...
    return (prev_enabled, next_enabled)

Advantages: fast (0.02s), no browser dependency, integrates with existing pytest setup.

Layer 2: Playwright Browser Integration Tests (DOM interaction verification)

Generate HTML with test data embedded, open it in real Chromium via Playwright, and verify DOM state and user interactions.

Best for: button disabled states, overlay show/hide, keyboard events, click navigation, etc.

Example: tests/test_nav_browser.py

Building test HTML

def _build_test_html():
    from claude_tap.viewer import VIEWER_SCRIPT_ANCHOR, _read_viewer_template

    template = _read_viewer_template()
    records = [json.dumps(e) for e in TEST_ENTRIES]
    data_js = "const EMBEDDED_TRACE_DATA = [\n" + ",\n".join(records) + "\n];\n"
    # Inject data into template
    return template.replace(
        VIEWER_SCRIPT_ANCHOR,
        f"<script>\n{data_js}</script>\n{VIEWER_SCRIPT_ANCHOR}",
        1,
    )

Playwright fixture

@pytest.fixture(scope="module")
def browser_page(html_file):
    from playwright.sync_api import sync_playwright
    pw = sync_playwright().start()
    browser = pw.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto(f"file://{html_file}")
    page.wait_for_selector(".sidebar-item", timeout=5000)
    yield page
    browser.close()
    pw.stop()

Verifying DOM state

def _get_nav_state(page):
    return page.evaluate("""() => {
        const overlay = document.querySelector('.diff-overlay');
        if (!overlay) return { overlayExists: false };
        return {
            overlayExists: true,
            prevDisabled: overlay.querySelector('.diff-nav-prev')?.disabled,
            nextDisabled: overlay.querySelector('.diff-nav-next')?.disabled,
            title: overlay.querySelector('.diff-title')?.textContent,
        };
    }""")

Simulating user interaction

# Click buttons
page.click(".diff-nav-next")
# Keyboard navigation
page.keyboard.press("ArrowRight")
# Call internal JS functions
page.evaluate("selectEntry(2)")
page.evaluate("showDiff()")

Notes

  • Trace data may contain </script> text (e.g., when Claude discusses code), which breaks <script> block parsing. Escape it: line.replace("</script>", '</scr" + "ipt>')
  • Test data must match the viewer's expected JSONL format (including turn, duration_ms, request_id, request.path, etc.)
  • Playwright requires uv pip install playwright

Running

# Fast unit tests
uv run pytest tests/test_diff_matching.py -v

# Browser integration tests
uv run pytest tests/test_nav_browser.py -v

# All (excluding slow e2e)
uv run pytest tests/ --ignore=tests/test_e2e.py -v
验证维护者文档结构、标准时效性、清单路径及计划状态。用于在修改 .agents/docs/ 下文件后,提前捕获元数据过时、路径断裂和状态漂移问题,避免 CI 失败。
修改 .agents/docs/standards/ 下的文件 修改 .agents/docs/plans/ 下的文件 修改 .agents/docs/architecture/ 下的文件 修改 AGENTS.md
.agents/skills/legibility-check/SKILL.md
npx skills add liaohch3/claude-tap --skill legibility-check -g -y
SKILL.md
Frontmatter
{
    "name": "legibility-check",
    "description": "Validate maintainer docs structure, standards freshness, manifest paths, and plan state. Run this after modifying any file under .agents\/docs\/standards\/, .agents\/docs\/plans\/, .agents\/docs\/architecture\/, or AGENTS.md — it catches stale metadata, broken manifest paths, and plan state drift before CI does.",
    "user_invocable": true
}

Legibility Check

Run deterministic checks for maintainer docs that mirror what CI enforces via .github/workflows/legibility.yml. Catching these locally saves a round-trip to CI.

What it checks

  1. Standards freshness — every .agents/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.
  2. Architecture manifest — every path listed in .agents/docs/architecture/manifest.yaml under expected_paths: must exist in the repo.
  3. Plan state drift — every .agents/docs/plans/**/*.md must have a status frontmatter field (active, completed, or cancelled). Completed plans must not contain unchecked - [ ] checkboxes (outside fenced code blocks).

Run

uv run python scripts/check_legibility.py

Options:

  • --freshness-days N — change the staleness threshold (default: 60)
  • --strict-freshness — promote stale warnings to failures
  • --repo-root PATH — override repo root (default: cwd)

Fixing common failures

Failure Fix
missing frontmatter key 'X' Add the missing key to the YAML frontmatter block at the top of the file
last_reviewed must be ISO date Use YYYY-MM-DD format
last_reviewed ... is stale Update last_reviewed to today's date after reviewing the content
expected path missing: X Either create the file or remove the stale entry from manifest.yaml
status must be one of [...] Add status: active (or completed/cancelled) to plan frontmatter
completed plan still contains unchecked TODO Check off remaining items or change status back to active

After fixing

Re-run the check to confirm all issues are resolved before committing:

uv run python scripts/check_legibility.py && echo "All clear"
基于Playwright录制浏览器测试视频,用于PR审查和Bug修复验证。支持无头模式生成.webm文件,通过添加暂停、结合终端日志断言及使用真实数据,提供直观的视觉证据以辅助代码评审和问题排查。
需要录制浏览器操作视频以进行PR审查 需要录制前后对比视频以验证Bug修复 需要为回归测试提供视觉证据
.agents/skills/playwright-screen-recording/SKILL.md
npx skills add liaohch3/claude-tap --skill playwright-screen-recording -g -y
SKILL.md
Frontmatter
{
    "name": "playwright-screen-recording",
    "description": "Record browser test videos with Playwright for PR review and bug fix verification",
    "user_invocable": false
}

Playwright Screen Recording for Test Verification

Use Playwright's video recording to capture headless browser operations as .webm videos for PR review or bug fix verification.

Core Usage

from playwright.sync_api import sync_playwright
import tempfile
from pathlib import Path

video_dir = Path(tempfile.mkdtemp())

with sync_playwright() as pw:
    browser = pw.chromium.launch(headless=True)
    context = browser.new_context(
        viewport={"width": 1400, "height": 900},
        record_video_dir=str(video_dir),
        record_video_size={"width": 1400, "height": 900},
    )
    page = context.new_page()
    page.goto(f"file:///path/to/test.html")

    # ... perform test actions, add pauses for readability ...
    page.wait_for_timeout(800)  # pause so viewers can see the current state

    page.close()
    context.close()  # video is finalized after context.close()
    browser.close()

# Retrieve the recorded video
videos = list(video_dir.glob("*.webm"))
if videos:
    videos[0].rename("demo.webm")

Use Cases

  • Bug fix verification: record before/after comparisons showing button state changes, UI behavior differences
  • PR Review: attach .webm video so reviewers can visually understand the change
  • Regression test evidence: record critical interaction paths as visual proof of passing tests

Recording Tips

Add pauses between actions

page.click(".some-button")
page.wait_for_timeout(800)   # let viewers see the click effect

page.keyboard.press("ArrowRight")
page.wait_for_timeout(600)   # let viewers see the navigation result

Combine assertions with terminal logging

state = get_nav_state(page)
print(f"[1] Title: {state['title']}")
print(f"    prev disabled: {state['prevDisabled']}  (expected: True)")
assert state["prevDisabled"] is True
print("    PASS")

Terminal output paired with the recorded video provides dual verification.

Prefer real data

Use existing real trace data from the project rather than synthetic data for more convincing demos:

# Build test HTML from a real trace file
records = []
with open(".traces/trace_xxx.jsonl") as f:
    for line in f:
        # Escape </script> to prevent breaking the HTML script block
        records.append(line.strip().replace("</script>", '</scr" + "ipt>'))

Notes

  • Video format is .webm (VP8 codec), supported by most players and browsers
  • Each page produces a separate video file
  • record_video_size controls video resolution — keep it consistent with viewport
  • Recording works in headless mode, no display required
  • Video files are typically a few hundred KB, suitable for attaching to PRs or chat
PR预检技能,用于在提交或合并前全面验证PR就绪状态。检查本地代码规范、CI状态、PR元数据及正文策略,判断是否具备合并条件,帮助在审查前发现潜在问题。
准备打开Pull Request 准备合并Pull Request 审查现有PR的准备情况
.agents/skills/pr-preflight/SKILL.md
npx skills add liaohch3/claude-tap --skill pr-preflight -g -y
SKILL.md
Frontmatter
{
    "name": "pr-preflight",
    "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.",
    "user_invocable": true
}

PR Preflight

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.

Check an existing PR

scripts/check_pr.sh <pr_number>

This runs:

  1. PR metadata — fetches title, state, draft status, merge state, branch info
  2. CI checks — counts pass/fail/pending GitHub Actions checks
  3. Local gates — runs lint, format, and tests locally:
    • uv run ruff check .
    • uv run ruff format --check .
    • uv run pytest tests/ -x --timeout=60
  4. PR body policy — validates required sections, evidence links, and blocked artifacts
  5. VerdictREADY or NOT_READY with specific reasons

Options

Flag Purpose
--repo OWNER/REPO Override repository (default: auto-detect via gh)
--no-tests Skip local test gates (useful when you just want CI + metadata check)

Exit codes

Code Meaning
0 All checks passed — ready to merge
1 Script error (missing tool, network failure)
2 Not ready — at least one check failed

Run local gates only (no PR needed)

If you haven't opened a PR yet and just want to validate locally:

uv run ruff check . && uv run ruff format --check . && uv run pytest tests/ -x --timeout=60

Or use the pre-commit hook (auto-runs lint on commit):

git config core.hooksPath .githooks

What blocks merge

The script reports NOT_READY if any of these are true:

  • PR is not in OPEN state
  • PR is still a draft
  • Merge state is not CLEAN or HAS_HOOKS
  • Any CI check is failing
  • Any CI check is still pending
  • Local gates (lint/format/tests) fail
  • PR body policy fails, such as missing evidence for runtime/viewer/client changes

Typical workflow

# 1. Make sure local gates pass
uv run ruff check . && uv run ruff format --check . && uv run pytest tests/ -x --timeout=60

# 2. Push and open PR
git push origin my-branch
gh pr create --title "feat: ..." --body "..."

# 3. Wait for CI, then run full preflight
scripts/check_pr.sh <pr_number>
用于将代码推送到GitHub并触发PyPI发布。若提交包含功能、修复或重构等实质性变更,需自动升级版本号以触发CI流水线生成新标签并发布;若仅为文档或测试更新则跳过版本升级,确保包版本与代码同步。
用户希望发布新版本到PyPI 用户希望推送代码到GitHub并触发CI构建
.agents/skills/push-release/SKILL.md
npx skills add liaohch3/claude-tap --skill push-release -g -y
SKILL.md
Frontmatter
{
    "name": "push-release",
    "description": "Push to GitHub and optionally bump version to trigger PyPI release",
    "user_invocable": true
}

Push & Release

Push code to GitHub. If the pending commits contain feature changes, bump the version number so CI auto-publishes to PyPI.

Workflow

  1. Check working tree: Ensure no uncommitted changes (prompt user to commit first if dirty).

  2. Determine whether a version bump is needed:

    • Read current version from pyproject.toml
    • Run git log origin/main..HEAD --oneline to inspect pending commits
    • If commits include feature changes (feat/fix/refactor, not purely docs/chore/test), a bump is needed
    • If only docs, tests, or CI changes, skip the bump
  3. If bump is needed:

    • Choose bump level based on change type:
      • patch (0.1.4 → 0.1.5): bug fixes, minor improvements
      • minor (0.1.4 → 0.2.0): new features
      • major (0.1.4 → 1.0.0): breaking changes
    • Update the version field in pyproject.toml
    • Update __version__ in claude_tap/__init__.py
    • git commit --amend to fold the version bump into the last commit (avoids extra commits)
  4. Push:

    git push origin main
    
  5. Confirm CI status:

Important: Version Bump = PyPI Release

The CI pipeline works as follows: push to main → auto-tag (only if version changed) → PyPI publish (triggered by new tag).

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.

  • Version numbers in pyproject.toml and claude_tap/__init__.py must stay in sync
  • Only skip the bump for pure docs/test/CI changes that don't affect the published package
对 Claude CLI 执行真实端到端测试,验证 trace 输出。支持 pytest 自动化模式(7个用例)和 tmux 交互式 TUI 模式,需安装 claude CLI、Python 依赖及 tmux。
需要运行真实的端到端测试以验证 Claude CLI 功能时 需要检查 Trace 输出或工具调用行为时
.agents/skills/real-e2e-test/SKILL.md
npx skills add liaohch3/claude-tap --skill real-e2e-test -g -y
SKILL.md
Frontmatter
{
    "name": "real-e2e-test",
    "tags": "testing, e2e, integration, tmux",
    "description": "Run real E2E tests against Claude CLI in pytest and tmux modes"
}

Real E2E Test Skill

Run real end-to-end tests that start claude-tap from local source, connect to the real Claude CLI, and verify trace output.

Prerequisites

  • claude CLI installed and authenticated
  • Python dev dependencies installed: uv sync --extra dev
  • tmux installed for interactive mode (brew install tmux)

Mode 1: Pytest Real E2E (7 test cases)

Run all real E2E tests

uv run pytest tests/e2e/ --run-real-e2e --timeout=300 -v

Run a single test

uv run pytest tests/e2e/test_real_proxy.py::TestRealProxy::test_single_turn --run-real-e2e --timeout=180 -v -s

Run with debug output

uv run pytest tests/e2e/ --run-real-e2e --timeout=300 -v -s --tb=long

Mode 2: tmux Interactive Real E2E

Use this when you need to validate non--p interactive behavior in Claude Code TUI.

scripts/run_real_e2e_tmux.sh

Optional overrides:

PROMPT_ONE="Use the shell tool to run command ls in the current directory, then reply with any 5 filenames only." \
PROMPT_TWO="Thank you." \
SUBMIT_KEY="Enter" \
PERMISSION_MODE="bypassPermissions" \
scripts/run_real_e2e_tmux.sh

Important tmux interaction notes:

  • Submit key is Enter for Claude Code TUI in tmux (confirmed working).
  • PROMPT_ONE should intentionally trigger tool use.
  • For portability, use grep -F instead of rg in shell assertions (rg may be unavailable).

Verification Checklist (for both modes)

  • Latest trace .jsonl contains both prompts (PROMPT_ONE, PROMPT_TWO)
  • At least 2 requests hit /v1/messages
  • At least one response content block has "type": "tool_use"
  • HTML viewer file is generated (trace_*.html)

Notes

  • Real E2E tests are skipped by default; --run-real-e2e is required.
  • Each pytest case starts a fresh proxy server and trace directory.
  • Timeouts are intentionally generous because real API calls are involved.
  • tmux mode includes retry logic for prompt submission and post-run JSONL assertions.

Pytest Test Cases

Test Timeout What It Tests
test_single_turn 180s Basic prompt/response trace capture
test_multi_turn 300s Conversation memory with -c flag
test_tool_use 180s Tool use generates multiple trace records
test_html_viewer_generated 180s HTML viewer generated with embedded trace data
test_api_key_redaction 180s API keys redacted from trace output
test_streaming_sse_capture 180s SSE events captured in streaming mode
test_trace_summary 180s CLI stdout includes trace summary and API call count
验证PR证据截图和Viewer HTML的质量。检查图片尺寸、大小及空白度,并使用Playwright验证HTML渲染正确性,防止CI失败或误导性证据。
添加或修改 .agents/evidence/pr/ 或 .agents/recordings/ 下的图像后 生成新的 viewer HTML 文件后
.agents/skills/screenshot-validation/SKILL.md
npx skills add liaohch3/claude-tap --skill screenshot-validation -g -y
SKILL.md
Frontmatter
{
    "name": "screenshot-validation",
    "description": "Validate screenshot and viewer HTML quality for PR evidence. Run this after adding or modifying images under .agents\/evidence\/pr\/ or .agents\/recordings\/, or after generating a new viewer HTML file. Combines image quality checks (resolution, blankness, file size) with Playwright-based viewer rendering verification.",
    "user_invocable": true
}

Screenshot Validation

Validate that evidence images and viewer HTML files meet quality standards before committing. This catches issues that would otherwise fail CI or produce misleading PR evidence.

Image quality check

Checks PNG/JPG/GIF/WEBP files for:

  • Minimum dimensions: 400x400 pixels (hard fail)
  • Desktop viewport width: >= 1280px (warning if narrower)
  • File size: <= 5MB (warning if larger)
  • Blankness detection (PNG only): fails if > 90% of pixels are white/transparent

Run on specific files or directories

uv run python scripts/check_screenshots.py .agents/evidence/pr/
uv run python scripts/check_screenshots.py .agents/recordings/
uv run python scripts/check_screenshots.py path/to/specific-image.png

Run on git-staged images

scripts/check_screenshots.sh

This shell wrapper automatically finds staged PNG/JPG files and runs the quality check on them — useful as a pre-commit sanity check.

Viewer HTML rendering verification

Uses Playwright (headless Chromium) to verify that generated viewer HTML files actually render correctly — not just raw JSON or Python errors.

Checks:

  • No JavaScript errors on page load
  • Normal traces render a sidebar with entries and a detail panel
  • Empty embedded traces render the explicit "No API calls captured" state
  • Body text doesn't contain raw JSON dumps or Python tracebacks

Run

uv run python scripts/verify_screenshots.py .traces/trace_*.html

Requires Playwright to be installed (uv pip install playwright && playwright install chromium).

Typical workflow

After generating new evidence for a PR:

# 1. Check image quality
uv run python scripts/check_screenshots.py .agents/evidence/pr/

# 2. If you generated new viewer HTML, verify it renders
uv run python scripts/verify_screenshots.py .traces/trace_*.html

# 3. If all passes, stage and commit
git add .agents/evidence/pr/

Fixing common failures

Failure Fix
very small image (WxH; minimum is 400x400) Retake screenshot at a larger viewport or higher resolution
narrow desktop viewport (Wpx < 1280px) Resize browser window to >= 1280px wide before capturing
mostly blank/white image Ensure the screenshot captures actual content, not an empty page
No sidebar — viewer not rendered Viewer HTML is broken; regenerate from trace JSONL
JS errors Check viewer.html for syntax errors in embedded data
自动补全 viewer_i18n.json 中缺失的多语言翻译。基于英文和中文源,通过 OpenRouter 生成日、韩、法、阿、德、俄文译文。支持干运行预览及模型覆盖。
新增或修改了 viewer_i18n.json 中的英文或中文 UI 字符串 需要为其他六种目标语言自动填充缺失的翻译键值
.agents/skills/translate-i18n/SKILL.md
npx skills add liaohch3/claude-tap --skill translate-i18n -g -y
SKILL.md
Frontmatter
{
    "name": "translate-i18n",
    "description": "Fill missing i18n translations in the viewer source JSON. Run this after adding or modifying English or Chinese UI strings in claude_tap\/viewer_i18n.json — it auto-translates to ja, ko, fr, ar, de, ru via OpenRouter.",
    "user_invocable": true
}

Translate i18n

Automatically fill missing translations for the viewer's claude_tap/viewer_i18n.json source file. The script uses English and Chinese as source languages and translates to Japanese, Korean, French, Arabic, German, and Russian.

Prerequisites

  • OPENROUTER_API_KEY must be set in the environment (it is in the user's .zshrc)
  • Default model: google/gemini-2.5-flash

Workflow

1. Check what's missing (dry run)

Always preview first to confirm which keys need translation:

uv run python scripts/translate_i18n.py --dry-run

This parses claude_tap/viewer_i18n.json, finds keys present in both en and zh-CN but missing in other languages, and lists them without modifying the file.

2. Run the translation

uv run python scripts/translate_i18n.py

The script calls OpenRouter once per target language, then writes the translations back into viewer_i18n.json in-place.

3. Verify the result

After translation, run the formatter and tests to make sure nothing broke:

uv run python -m json.tool claude_tap/viewer_i18n.json >/dev/null
uv run pytest tests/test_translate_i18n.py -v

Options

Flag Purpose
--dry-run Show missing keys only, no file changes
--model MODEL Override the OpenRouter model (default: google/gemini-2.5-flash)
--target {viewer,cli} Translation target preset (default: viewer)
--file PATH Override target file path
--object-name NAME Override the legacy JS/Python i18n object name

How it works

The script:

  1. Loads the viewer i18n JSON source file
  2. Validates that every language block is a string-to-string map
  3. Identifies keys present in en + zh-CN but missing in target languages
  4. Sends a structured prompt to OpenRouter with existing translations for consistency
  5. Normalizes fullwidth punctuation for CJK languages (matching zh-CN style)
  6. Inserts new entries after the existing keys in each target language

Common scenarios

Added a new UI string: Add the key to both en and zh-CN blocks in claude_tap/viewer_i18n.json, then run this skill. The other 6 languages will be filled automatically.

Changed an existing string: The script only fills missing keys. To re-translate an existing key, first delete it from the target language blocks, then run the script.

用于在修改 claude-tap 核心逻辑后运行端到端测试,覆盖代理处理、追踪写入、HTML 查看器及版本检查等功能。通过 pytest 执行测试并分析失败原因以定位问题。
修改了 claude-tap 的核心逻辑代码 需要验证端到端功能的正确性
.agents/skills/e2e-test/SKILL.md
npx skills add liaohch3/claude-tap --skill e2e-test -g -y
SKILL.md
Frontmatter
{
    "name": "e2e-test",
    "description": "Run claude-tap end-to-end tests with pytest",
    "user_invocable": true
}

claude-tap E2E Test

Run this skill after modifying core logic in claude-tap, especially:

  • Proxy handler / SSE reassembly (__init__.py)
  • TraceWriter (JSONL writing, flush behavior)
  • HTML viewer generation (viewer.html, _generate_html_viewer)
  • LiveViewerServer (SSE streaming)
  • Signal handling / graceful shutdown
  • Smart update check / trace cleanup

Steps

  1. Run the full test suite:
uv run pytest tests/test_e2e.py -v --timeout=120

Or run a single test:

uv run pytest tests/test_e2e.py::test_e2e -v           # Full E2E pipeline
uv run pytest tests/test_e2e.py::test_trace_cleanup -v  # Trace cleanup
uv run pytest tests/test_e2e.py::test_version_check_with_fake_pypi -v  # Update check
  1. Read the output. Each test prints PASSED or FAILED.

  2. If tests fail, check:

    • test_e2e fails: Core proxy pipeline issue. Check proxy_handler, _handle_streaming, TraceWriter.write.
    • test_trace_cleanup fails: Manifest logic issue. Check _load_manifest, _cleanup_traces, _register_trace.
    • test_version_check_ fails*: PyPI check logic. Check _check_pypi_version, CLAUDE_TAP_PYPI_URL env var.
    • test_live_viewer_ fails*: Viewer HTML issues. Check viewer.html for preserveDetail chain, updateNavButtons.
    • Timeout: May be a network/port issue, not a claude-tap bug.

Accueil - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-07-09 09:12
浙ICP备14020137号-1 $Carte des visiteurs$