chore: remove outdated pr-review skill (#3028)

* chore: remove outdated pr-review skill

The .qwen/skills/pr-review skill is outdated and no longer needed.
Also removes the dangling reference from terminal-capture skill.

* chore: add token-stats script to e2e-testing skill

* fix(test): remove flaky AskUserQuestionDialog Submit tab test

The "shows Submit tab for multiple questions" test fails on macOS CI
due to terminal width truncation causing "Submit answers" to render
as just "Submit".
This commit is contained in:
tanzhenxin 2026-04-09 10:54:10 +08:00 committed by GitHub
parent a0d08aa68d
commit e0aeee5414
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 92 additions and 135 deletions

View file

@ -156,3 +156,24 @@ tmux kill-session -t test
For testing MCP tool behavior end-to-end, read `references/mcp-testing.md`. It
covers the setup gotchas (config location, git repo requirement) and includes
a reusable zero-dependency test server template in `scripts/mcp-test-server.js`.
## Token Usage Stats
Use `scripts/token-stats.py` to summarize token usage across recent API logs:
```bash
python3 .qwen/skills/e2e-testing/scripts/token-stats.py 20 # last 20 requests
```
Shows input, cached, and output tokens per request with cache hit rates. Useful
for verifying prompt caching behavior or investigating unexpected token counts.
## Tips
- Use interactive (tmux) mode when the bug involves permission prompts, slash
commands, or keyboard interactions. Headless mode has no TUI — these don't
exist there.
- Use interactive (tmux) mode for hang-related issues. Headless mode produces
no output when the process stalls, giving you nothing to work with.
- Use `--approval-mode default` when testing permission rules. `yolo` bypasses
rule evaluation entirely — it can't test whether a rule matches.

View file

@ -0,0 +1,70 @@
#!/usr/bin/env python3
"""Display token usage stats from the last X request logs in ~/.qwen/logs."""
import argparse
import json
from pathlib import Path
def parse_args():
p = argparse.ArgumentParser(description="Show token stats from qwen request logs")
p.add_argument("count", nargs="?", type=int, default=10, help="Number of recent logs to show (default: 10)")
p.add_argument("--log-dir", default=Path.home() / ".qwen" / "logs", type=Path)
return p.parse_args()
def load_logs(log_dir: Path, count: int):
files = sorted(log_dir.glob("*.json"))
for f in files[-count:]:
try:
with open(f) as fh:
yield json.load(fh), f.name
except (json.JSONDecodeError, OSError):
continue
def main():
args = parse_args()
if not args.log_dir.is_dir():
print(f"Log directory not found: {args.log_dir}")
return
rows = []
total_input = total_cached = total_output = 0
for data, fname in load_logs(args.log_dir, args.count):
ts = data.get("timestamp", "?")
model = data.get("request", {}).get("model", "?")
usage = data.get("response", {}).get("usage", {})
input_tok = usage.get("prompt_tokens", 0)
output_tok = usage.get("completion_tokens", 0)
cached_tok = usage.get("prompt_tokens_details", {}).get("cached_tokens", 0)
cache_rate = (cached_tok / input_tok * 100) if input_tok else 0
total_input += input_tok
total_cached += cached_tok
total_output += output_tok
rows.append((ts, model, input_tok, cached_tok, output_tok, cache_rate))
if not rows:
print("No logs found.")
return
# Print table
hdr = f"{'Timestamp':<28} {'Model':<16} {'Input':>8} {'Cached':>8} {'Output':>8} {'Cache%':>7}"
sep = "-" * len(hdr)
print(hdr)
print(sep)
for ts, model, inp, cached, out, rate in rows:
print(f"{ts:<28} {model:<16} {inp:>8,} {cached:>8,} {out:>8,} {rate:>6.1f}%")
# Totals
print(sep)
overall_rate = (total_cached / total_input * 100) if total_input else 0
print(f"{'TOTAL':<28} {'':<16} {total_input:>8,} {total_cached:>8,} {total_output:>8,} {overall_rate:>6.1f}%")
if __name__ == "__main__":
main()