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.
Files changed (239) hide show
  1. claw_tap-0.0.5/.agents/skills/codex-e2e-test/SKILL.md +128 -0
  2. claw_tap-0.0.5/.agents/skills/demo-video/SKILL.md +86 -0
  3. claw_tap-0.0.5/.agents/skills/e2e-test/SKILL.md +40 -0
  4. claw_tap-0.0.5/.agents/skills/js-in-html-testing/SKILL.md +120 -0
  5. claw_tap-0.0.5/.agents/skills/legibility-check/SKILL.md +45 -0
  6. claw_tap-0.0.5/.agents/skills/playwright-screen-recording/SKILL.md +92 -0
  7. claw_tap-0.0.5/.agents/skills/pr-preflight/SKILL.md +79 -0
  8. claw_tap-0.0.5/.agents/skills/push-release/SKILL.md +46 -0
  9. claw_tap-0.0.5/.agents/skills/real-e2e-test/SKILL.md +83 -0
  10. claw_tap-0.0.5/.agents/skills/screenshot-validation/SKILL.md +76 -0
  11. claw_tap-0.0.5/.agents/skills/translate-i18n/SKILL.md +69 -0
  12. claw_tap-0.0.5/.all-contributorsrc +72 -0
  13. claw_tap-0.0.5/.claude/CLAUDE.md +12 -0
  14. claw_tap-0.0.5/.dockerignore +13 -0
  15. claw_tap-0.0.5/.githooks/pre-commit +12 -0
  16. claw_tap-0.0.5/.github/ISSUE_TEMPLATE/bug_report.yml +86 -0
  17. claw_tap-0.0.5/.github/ISSUE_TEMPLATE/config.yml +5 -0
  18. claw_tap-0.0.5/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
  19. claw_tap-0.0.5/.github/ISSUE_TEMPLATE.md +24 -0
  20. claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/chore.md +30 -0
  21. claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/docs.md +20 -0
  22. claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/feature.md +35 -0
  23. claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/fix.md +37 -0
  24. claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/plan.md +20 -0
  25. claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/refactor.md +30 -0
  26. claw_tap-0.0.5/.github/PULL_REQUEST_TEMPLATE/test.md +33 -0
  27. claw_tap-0.0.5/.github/pull_request_template.md +22 -0
  28. claw_tap-0.0.5/.github/workflows/auto-release.yml +94 -0
  29. claw_tap-0.0.5/.github/workflows/ci.yml +54 -0
  30. claw_tap-0.0.5/.github/workflows/legibility.yml +18 -0
  31. claw_tap-0.0.5/.github/workflows/publish.yml +145 -0
  32. claw_tap-0.0.5/.gitignore +98 -0
  33. claw_tap-0.0.5/.pre-commit-config.yaml +33 -0
  34. claw_tap-0.0.5/.python-version +1 -0
  35. claw_tap-0.0.5/AGENTS.md +85 -0
  36. claw_tap-0.0.5/CHANGELOG.md +375 -0
  37. claw_tap-0.0.5/CODE_OF_CONDUCT.md +35 -0
  38. claw_tap-0.0.5/CONTRIBUTING.md +68 -0
  39. claw_tap-0.0.5/Dockerfile +19 -0
  40. claw_tap-0.0.5/LICENSE +21 -0
  41. claw_tap-0.0.5/PKG-INFO +259 -0
  42. claw_tap-0.0.5/README.md +226 -0
  43. claw_tap-0.0.5/README_zh.md +220 -0
  44. claw_tap-0.0.5/SECURITY.md +40 -0
  45. claw_tap-0.0.5/claude_tap/__init__.py +47 -0
  46. claw_tap-0.0.5/claude_tap/__main__.py +36 -0
  47. claw_tap-0.0.5/claude_tap/certs.py +212 -0
  48. claw_tap-0.0.5/claude_tap/claw_session.py +42 -0
  49. claw_tap-0.0.5/claude_tap/cli.py +830 -0
  50. claw_tap-0.0.5/claude_tap/cursor_transcript.py +206 -0
  51. claw_tap-0.0.5/claude_tap/export.py +266 -0
  52. claw_tap-0.0.5/claude_tap/forward_proxy.py +822 -0
  53. claw_tap-0.0.5/claude_tap/live.py +308 -0
  54. claw_tap-0.0.5/claude_tap/proxy.py +760 -0
  55. claw_tap-0.0.5/claude_tap/py.typed +0 -0
  56. claw_tap-0.0.5/claude_tap/session_dispatcher.py +133 -0
  57. claw_tap-0.0.5/claude_tap/session_index.py +217 -0
  58. claw_tap-0.0.5/claude_tap/sse.py +243 -0
  59. claw_tap-0.0.5/claude_tap/trace.py +80 -0
  60. claw_tap-0.0.5/claude_tap/viewer.html +4154 -0
  61. claw_tap-0.0.5/claude_tap/viewer.py +366 -0
  62. claw_tap-0.0.5/claw_tap.egg-info/PKG-INFO +259 -0
  63. claw_tap-0.0.5/claw_tap.egg-info/SOURCES.txt +238 -0
  64. claw_tap-0.0.5/claw_tap.egg-info/dependency_links.txt +1 -0
  65. claw_tap-0.0.5/claw_tap.egg-info/entry_points.txt +2 -0
  66. claw_tap-0.0.5/claw_tap.egg-info/requires.txt +10 -0
  67. claw_tap-0.0.5/claw_tap.egg-info/top_level.txt +1 -0
  68. claw_tap-0.0.5/docker-compose.yml +30 -0
  69. claw_tap-0.0.5/docs/architecture/manifest.yaml +10 -0
  70. claw_tap-0.0.5/docs/architecture.png +0 -0
  71. claw_tap-0.0.5/docs/architecture.svg +198 -0
  72. claw_tap-0.0.5/docs/billing-header-diff.png +0 -0
  73. claw_tap-0.0.5/docs/demo.gif +0 -0
  74. claw_tap-0.0.5/docs/demo.mp4 +0 -0
  75. claw_tap-0.0.5/docs/demo_zh.gif +0 -0
  76. claw_tap-0.0.5/docs/demo_zh.mp4 +0 -0
  77. claw_tap-0.0.5/docs/deploy-compose.md +30 -0
  78. claw_tap-0.0.5/docs/diff-modal.png +0 -0
  79. claw_tap-0.0.5/docs/error-experience/entries/2026-02-25-pr1-stale-base.md +36 -0
  80. claw_tap-0.0.5/docs/error-experience/entries/2026-02-26-codex-sandbox-git-blocked.md +28 -0
  81. claw_tap-0.0.5/docs/error-experience/entries/2026-02-26-codex-sandbox-tmux-blocked.md +26 -0
  82. claw_tap-0.0.5/docs/error-experience/entries/2026-02-26-hardcoded-version-string.md +23 -0
  83. claw_tap-0.0.5/docs/error-experience/entries/2026-02-26-python313-ssl-aki-required.md +68 -0
  84. claw_tap-0.0.5/docs/error-experience/entries/2026-02-26-rg-not-portable.md +33 -0
  85. claw_tap-0.0.5/docs/error-experience/entries/2026-02-26-sigttou-suspend-on-exit.md +30 -0
  86. claw_tap-0.0.5/docs/error-experience/entries/2026-02-27-pr-screenshot-cache-stale.md +35 -0
  87. claw_tap-0.0.5/docs/error-experience/entries/2026-02-28-codex-reverse-websocket-capture-gap.md +35 -0
  88. claw_tap-0.0.5/docs/error-experience/entries/2026-03-03-codex-ws-timeout-fallback-evidence.md +36 -0
  89. claw_tap-0.0.5/docs/error-experience/entries/2026-03-03-pr-screenshot-quality-failures.md +57 -0
  90. claw_tap-0.0.5/docs/error-experience/entries/2026-03-03-pr22-websocket-screenshot-quality-regression.md +43 -0
  91. claw_tap-0.0.5/docs/error-experience/entries/2026-03-03-ws-proxy-debugging-failure.md +92 -0
  92. claw_tap-0.0.5/docs/error-experience/entries/2026-03-10-codex-strip-prefix-url-mismatch.md +61 -0
  93. claw_tap-0.0.5/docs/evidence/PR44_VERIFY_REPORT.md +72 -0
  94. claw_tap-0.0.5/docs/evidence/WS_VERIFY_REPORT.md +86 -0
  95. claw_tap-0.0.5/docs/evidence/bedrock-default-filter/sidebar-default-main-turns.png +0 -0
  96. claw_tap-0.0.5/docs/evidence/bedrock-eventstream-viewer/bedrock-response-viewer.png +0 -0
  97. claw_tap-0.0.5/docs/evidence/issue87/codex-real-e2e-viewer.png +0 -0
  98. claw_tap-0.0.5/docs/evidence/issue87/codex-sdk-real-e2e-viewer.png +0 -0
  99. claw_tap-0.0.5/docs/evidence/issue87/codex-sdk-real-ws-continuation-warning.png +0 -0
  100. claw_tap-0.0.5/docs/evidence/issue87/codex-sdk-real-ws-message-history.png +0 -0
  101. claw_tap-0.0.5/docs/evidence/pr22/trace-viewer-summary.png +0 -0
  102. claw_tap-0.0.5/docs/evidence/pr22/ws-upgrade-log.png +0 -0
  103. claw_tap-0.0.5/docs/evidence/pr22-websocket-last-turn-wide.png +0 -0
  104. claw_tap-0.0.5/docs/evidence/pr22-websocket-viewer-wide.png +0 -0
  105. claw_tap-0.0.5/docs/evidence/pr26-test-evidence.png +0 -0
  106. claw_tap-0.0.5/docs/evidence/pr44/pr44-turn2-messages.png +0 -0
  107. claw_tap-0.0.5/docs/evidence/pr44/pr44-turn2-response.png +0 -0
  108. claw_tap-0.0.5/docs/evidence/pr63/pr63-collapsed.png +0 -0
  109. claw_tap-0.0.5/docs/evidence/pr63/pr63-expanded.png +0 -0
  110. claw_tap-0.0.5/docs/evidence/pr73/README.md +134 -0
  111. claw_tap-0.0.5/docs/evidence/pr73/pr73-cxx-forward-websocket-fixed-output.png +0 -0
  112. claw_tap-0.0.5/docs/evidence/pr73/pr73-cxx-forward-websocket.png +0 -0
  113. claw_tap-0.0.5/docs/evidence/pr73/pr73-direct-forward-websocket-fixed-output.png +0 -0
  114. claw_tap-0.0.5/docs/evidence/pr73/pr73-direct-forward-websocket.png +0 -0
  115. claw_tap-0.0.5/docs/evidence/pr74/README.md +69 -0
  116. claw_tap-0.0.5/docs/evidence/pr74/pr74-cxx-first-message-no-reconnect.png +0 -0
  117. claw_tap-0.0.5/docs/evidence/pr75/README.md +57 -0
  118. claw_tap-0.0.5/docs/evidence/pr75/pr75-codex-request-context-label.png +0 -0
  119. claw_tap-0.0.5/docs/evidence/pr79/export-html-viewer.png +0 -0
  120. claw_tap-0.0.5/docs/evidence/pr94/dashboard-real-traces.png +0 -0
  121. claw_tap-0.0.5/docs/evidence/pr99/viewer-real-codex-tool-name-fallback.png +0 -0
  122. claw_tap-0.0.5/docs/evidence/pr99/viewer-tool-name-fallback.png +0 -0
  123. claw_tap-0.0.5/docs/gen_architecture.py +65 -0
  124. claw_tap-0.0.5/docs/good-experience/entries/2026-02-25-mock-e2e-pattern.md +48 -0
  125. claw_tap-0.0.5/docs/good-experience/entries/2026-02-25-proxy-e2e-ci-hardening.md +43 -0
  126. claw_tap-0.0.5/docs/good-experience/entries/2026-02-26-tmux-e2e-success.md +40 -0
  127. claw_tap-0.0.5/docs/good-experience/entries/2026-02-27-viewer-sticky-validation-flow.md +25 -0
  128. claw_tap-0.0.5/docs/guides/OPENCLAW_README.md +154 -0
  129. claw_tap-0.0.5/docs/guides/engineering-practices.md +130 -0
  130. claw_tap-0.0.5/docs/guides/new-client-integration-playbook.md +190 -0
  131. claw_tap-0.0.5/docs/guides/viewer-ux-rationale.md +42 -0
  132. claw_tap-0.0.5/docs/images/viewer-brand-refresh-real-e2e-2turn-v1.png +0 -0
  133. claw_tap-0.0.5/docs/images/viewer-brand-refresh-real-e2e-tmux-v1.png +0 -0
  134. claw_tap-0.0.5/docs/images/viewer-brand-refresh-real-v1.png +0 -0
  135. claw_tap-0.0.5/docs/images/viewer-brand-refresh-v1.png +0 -0
  136. claw_tap-0.0.5/docs/images/viewer-sticky-actionbar-scrolled.png +0 -0
  137. claw_tap-0.0.5/docs/images/viewer-sticky-actionbar-top.png +0 -0
  138. claw_tap-0.0.5/docs/images/viewer-sticky-after-v2.png +0 -0
  139. claw_tap-0.0.5/docs/images/viewer-sticky-after.png +0 -0
  140. claw_tap-0.0.5/docs/images/viewer-sticky-before-v2.png +0 -0
  141. claw_tap-0.0.5/docs/images/viewer-sticky-before.png +0 -0
  142. claw_tap-0.0.5/docs/plans/2026-02-27-codex-support-handoff.md +243 -0
  143. claw_tap-0.0.5/docs/plans/2026-02-27-codex-support-plan.md +58 -0
  144. claw_tap-0.0.5/docs/plans/2026-02-27-viewer-sticky-e2e-workflow.md +51 -0
  145. claw_tap-0.0.5/docs/plans/2026-02-28-codex-websocket-support.md +59 -0
  146. claw_tap-0.0.5/docs/recordings/01_error_banner_and_sidebar.png +0 -0
  147. claw_tap-0.0.5/docs/recordings/02_sidebar_error_styling.png +0 -0
  148. claw_tap-0.0.5/docs/recordings/03_copy_button_fallback_success.png +0 -0
  149. claw_tap-0.0.5/docs/recordings/codex-demo.cast +25 -0
  150. claw_tap-0.0.5/docs/recordings/codex-demo.gif +0 -0
  151. claw_tap-0.0.5/docs/recordings/diff-scroll-bottom.png +0 -0
  152. claw_tap-0.0.5/docs/recordings/diff-scroll-top.png +0 -0
  153. claw_tap-0.0.5/docs/recordings/record_viewer.py +170 -0
  154. claw_tap-0.0.5/docs/recordings/search-01-open.png +0 -0
  155. claw_tap-0.0.5/docs/recordings/search-02-highlight.png +0 -0
  156. claw_tap-0.0.5/docs/recordings/search-03-navigate.png +0 -0
  157. claw_tap-0.0.5/docs/recordings/viewer-01-turn1-overview.png +0 -0
  158. claw_tap-0.0.5/docs/recordings/viewer-02-tools-sse-expanded.png +0 -0
  159. claw_tap-0.0.5/docs/recordings/viewer-03-request-json-scrolled.png +0 -0
  160. claw_tap-0.0.5/docs/recordings/viewer-04-turn5.png +0 -0
  161. claw_tap-0.0.5/docs/recordings/viewer-05-diff.png +0 -0
  162. claw_tap-0.0.5/docs/recordings/viewer-06-curl.png +0 -0
  163. claw_tap-0.0.5/docs/recordings/viewer-07-turn10.png +0 -0
  164. claw_tap-0.0.5/docs/recordings/viewer-08-dark-mode.png +0 -0
  165. claw_tap-0.0.5/docs/recordings/viewer-09-sidebar-scrolled.png +0 -0
  166. claw_tap-0.0.5/docs/recordings/viewer-10-last-turn.png +0 -0
  167. claw_tap-0.0.5/docs/recordings/viewer-11-final-wide.png +0 -0
  168. claw_tap-0.0.5/docs/recordings/viewer-codex-01-overview.png +0 -0
  169. claw_tap-0.0.5/docs/recordings/viewer-codex-02-messages.png +0 -0
  170. claw_tap-0.0.5/docs/recordings/viewer-codex-02-system-prompt.png +0 -0
  171. claw_tap-0.0.5/docs/recordings/viewer-codex-03-messages.png +0 -0
  172. claw_tap-0.0.5/docs/recordings/viewer-codex-03-scrolled.png +0 -0
  173. claw_tap-0.0.5/docs/recordings/viewer-codex-04-response.png +0 -0
  174. claw_tap-0.0.5/docs/recordings/viewer-codex-04-turn3.png +0 -0
  175. claw_tap-0.0.5/docs/recordings/viewer-codex-05-diff.png +0 -0
  176. claw_tap-0.0.5/docs/recordings/viewer-codex-05-tokens.png +0 -0
  177. claw_tap-0.0.5/docs/recordings/viewer-codex-06-diff.png +0 -0
  178. claw_tap-0.0.5/docs/recordings/viewer-codex-08-tools.png +0 -0
  179. claw_tap-0.0.5/docs/recordings/viewer-demo.mp4 +0 -0
  180. claw_tap-0.0.5/docs/standards/README.md +20 -0
  181. claw_tap-0.0.5/docs/standards/coding-and-runtime.md +35 -0
  182. claw_tap-0.0.5/docs/standards/debugging-standards.md +104 -0
  183. claw_tap-0.0.5/docs/standards/e2e-and-evidence.md +70 -0
  184. claw_tap-0.0.5/docs/standards/hard-rules.md +18 -0
  185. claw_tap-0.0.5/docs/standards/screenshot-standards.md +41 -0
  186. claw_tap-0.0.5/docs/standards/validation-and-gates.md +35 -0
  187. claw_tap-0.0.5/docs/standards/workflow-and-review.md +48 -0
  188. claw_tap-0.0.5/docs/support-matrix.md +117 -0
  189. claw_tap-0.0.5/docs/viewer-dark.png +0 -0
  190. claw_tap-0.0.5/docs/viewer-light.png +0 -0
  191. claw_tap-0.0.5/docs/viewer-zh.png +0 -0
  192. claw_tap-0.0.5/pyproject.toml +100 -0
  193. claw_tap-0.0.5/scripts/README.md +62 -0
  194. claw_tap-0.0.5/scripts/check_changelog.py +90 -0
  195. claw_tap-0.0.5/scripts/check_legibility.py +257 -0
  196. claw_tap-0.0.5/scripts/check_pr.sh +265 -0
  197. claw_tap-0.0.5/scripts/check_screenshots.py +370 -0
  198. claw_tap-0.0.5/scripts/check_screenshots.sh +139 -0
  199. claw_tap-0.0.5/scripts/run_real_e2e.sh +117 -0
  200. claw_tap-0.0.5/scripts/run_real_e2e_tmux.sh +158 -0
  201. claw_tap-0.0.5/scripts/translate_i18n.py +407 -0
  202. claw_tap-0.0.5/scripts/update_changelog.py +101 -0
  203. claw_tap-0.0.5/scripts/verify_screenshots.py +61 -0
  204. claw_tap-0.0.5/setup.cfg +4 -0
  205. claw_tap-0.0.5/tests/__init__.py +0 -0
  206. claw_tap-0.0.5/tests/conftest.py +73 -0
  207. claw_tap-0.0.5/tests/e2e/README.md +91 -0
  208. claw_tap-0.0.5/tests/e2e/__init__.py +1 -0
  209. claw_tap-0.0.5/tests/e2e/conftest.py +82 -0
  210. claw_tap-0.0.5/tests/e2e/test_real_proxy.py +243 -0
  211. claw_tap-0.0.5/tests/fixtures/codex_ws_multi_response_trace.jsonl +1 -0
  212. claw_tap-0.0.5/tests/fixtures/openai_responses_trace.jsonl +1 -0
  213. claw_tap-0.0.5/tests/tap_cli.py +45 -0
  214. claw_tap-0.0.5/tests/test_bedrock_viewer.py +192 -0
  215. claw_tap-0.0.5/tests/test_chat_completions_sse.py +215 -0
  216. claw_tap-0.0.5/tests/test_check_changelog.py +49 -0
  217. claw_tap-0.0.5/tests/test_check_legibility.py +178 -0
  218. claw_tap-0.0.5/tests/test_check_pr_script.py +151 -0
  219. claw_tap-0.0.5/tests/test_check_screenshots.py +98 -0
  220. claw_tap-0.0.5/tests/test_claw_session_and_dispatcher.py +93 -0
  221. claw_tap-0.0.5/tests/test_codex_launch.py +127 -0
  222. claw_tap-0.0.5/tests/test_cursor_launch.py +174 -0
  223. claw_tap-0.0.5/tests/test_diff_matching.py +446 -0
  224. claw_tap-0.0.5/tests/test_e2e.py +2696 -0
  225. claw_tap-0.0.5/tests/test_export.py +139 -0
  226. claw_tap-0.0.5/tests/test_nav_browser.py +311 -0
  227. claw_tap-0.0.5/tests/test_opencode_launch.py +102 -0
  228. claw_tap-0.0.5/tests/test_opencode_viewer.py +69 -0
  229. claw_tap-0.0.5/tests/test_path_allowlist.py +52 -0
  230. claw_tap-0.0.5/tests/test_perf_viewer.py +336 -0
  231. claw_tap-0.0.5/tests/test_responses_browser.py +334 -0
  232. claw_tap-0.0.5/tests/test_responses_support.py +127 -0
  233. claw_tap-0.0.5/tests/test_search_browser.py +458 -0
  234. claw_tap-0.0.5/tests/test_translate_i18n.py +179 -0
  235. claw_tap-0.0.5/tests/test_update_changelog.py +54 -0
  236. claw_tap-0.0.5/tests/test_viewer_non_dict_body.py +203 -0
  237. claw_tap-0.0.5/tests/test_windows_compat.py +99 -0
  238. claw_tap-0.0.5/tests/test_ws_proxy.py +504 -0
  239. 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