Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .qwen/skills/e2e-testing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
70 changes: 70 additions & 0 deletions .qwen/skills/e2e-testing/scripts/token-stats.py
Original file line number Diff line number Diff line change
@@ -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()
104 changes: 0 additions & 104 deletions .qwen/skills/pr-review/SKILL.md

This file was deleted.

2 changes: 1 addition & 1 deletion .qwen/skills/terminal-capture/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ export default [

## Integration with PR Review

This tool is commonly used for visual verification during PR reviews. For the complete code review + screenshot workflow, see the [pr-review](../pr-review/SKILL.md) skill.
This tool is commonly used for visual verification during PR reviews.

## Troubleshooting

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,36 +219,6 @@ describe('<AskUserQuestionDialog />', () => {
});

describe('multiple questions', () => {
it('shows Submit tab for multiple questions', async () => {
const onConfirm = vi.fn();
const details = createConfirmationDetails({
questions: [
createSingleQuestion({ header: 'Q1' }),
createSingleQuestion({ header: 'Q2' }),
],
});

const { stdin, lastFrame, unmount } = renderWithProviders(
<AskUserQuestionDialog
confirmationDetails={details}
onConfirm={onConfirm}
/>,
);
await wait();

// Navigate to submit tab (right arrow twice: Q1 -> Q2 -> Submit)
stdin.write('\u001B[C'); // Right
await wait();
stdin.write('\u001B[C'); // Right
await wait();

const output = lastFrame();
expect(output).toContain('Submit answers');
expect(output).toContain('Cancel');
expect(output).toContain('Your answers');
unmount();
});

it('shows unanswered questions as (not answered) in Submit tab', async () => {
const onConfirm = vi.fn();
const details = createConfirmationDetails({
Expand Down
Loading