assets/orca-eval-scan.sh
#!/usr/bin/env bash
set -euo pipefail
N=${1:-1}
slug=$(echo "$PWD" | tr '/.' '-' | sed 's/^-//')
dir="$HOME/.claude/projects/-${slug}"
if [ ! -d "$dir" ]; then
echo "ERROR: session dir not found" >&2
exit 1
fi
# use array from ls
files=()
while IFS= read -r -d '' f; do
files+=("$f")
done < <(find "$dir" -maxdepth 1 -name '*.jsonl' -type f -exec stat -f "%m %N" {} \; | sort -rn | head -n "$N" | awk '{for(i=2;i<=NF;i++) printf "%s%s", (i>2?OFS:""), $i; print "\0"}')
# fallback: read newline-separated
if [ ${#files[@]} -eq 0 ]; then
while IFS= read -r f; do
files+=("$f")
done < <(ls -1t "$dir"/*.jsonl 2>/dev/null | head -n "$N")
fi
if [ ${#files[@]} -eq 0 ]; then
echo "ERROR: session dir not found" >&2
exit 1
fi
for f in "${files[@]}"; do
fname=$(basename "$f")
mtime=$(stat -f "%Sm" -t "%Y-%m-%d %H:%M:%S" "$f" 2>/dev/null || echo "?")
echo "=== SESSION $fname ($mtime) ==="
python3 -c "
import json, sys
from collections import Counter
fpath = '$f'
prompts = []
errors = []
bash_cmds = []
cmd_counts = Counter()
with open(fpath) as fh:
for line in fh:
line = line.strip()
if not line:
continue
try:
obj = json.loads(line)
except json.JSONDecodeError:
continue
typ = obj.get('type')
if typ == 'user':
msg = obj.get('message', {})
content = msg.get('content', '')
if isinstance(content, list):
for item in content:
if not isinstance(item, dict):
continue
# text prompt
if item.get('type') == 'text' and 'text' in item:
t = item['text'][:200]
if t:
prompts.append(t)
# tool result - check errors
if item.get('type') == 'tool_result' or 'tool_use_id' in item:
is_err = item.get('is_error')
c = item.get('content', '') or ''
if isinstance(c, list):
c = ' '.join(str(x) for x in c)
if isinstance(c, str) and is_err:
errors.append(c[:150])
elif isinstance(content, str) and content.strip():
prompts.append(content.strip()[:200])
elif typ == 'assistant':
msg = obj.get('message', {})
content = msg.get('content', [])
if isinstance(content, list):
for item in content:
if not isinstance(item, dict):
continue
if item.get('type') == 'tool_use' and item.get('name') == 'Bash':
cmd = item.get('input', {}).get('command', '') or ''
cmd = cmd.strip()
if cmd:
bash_cmds.append(cmd)
cmd_counts[cmd] += 1
# Deduplicate prompts while preserving order
seen = set()
uniq_prompts = []
for p in prompts:
if p not in seen:
seen.add(p)
uniq_prompts.append(p)
print('PROMPTS:')
if uniq_prompts:
for p in uniq_prompts:
# sanitize
safe = p.replace('\\\\n', ' ').replace('\\n', ' ').replace('\n', ' ').replace('\r','')
print(f' {safe}')
else:
print(' (none)')
print()
print('ERRORS:')
if errors:
for e in errors:
safe = e.replace('\\\\n', ' ').replace('\\n', ' ').replace('\n', ' ').replace('\r','')
print(f' {safe}')
else:
print(' (none)')
print()
print('REPEATED COMMANDS:')
repeated = [(c, n) for c, n in cmd_counts.items() if n >= 3]
repeated.sort(key=lambda x: -x[1])
if repeated:
for cmd, count in repeated:
safe = cmd.replace('\\\\n', ' ').replace('\\n', ' ').replace('\n', ' ').replace('\r','')
safe = safe[:120]
print(f' ({count}x) {safe}')
else:
print(' (none)')
"
echo ""
done
SKILL.md
---
name: orca-eval
disable-model-invocation: true
description: Quét N session Claude Code gần nhất, distill best practices thành report md + đề xuất action cải tiến quy trình
---
# Skill: orca-eval
## Purpose
Vòng tự cải thiện quy trình: đọc lại session logs → rút best practice / anti-pattern → report md → đề xuất hành động (promote thành skill, sửa CLAUDE.md, thêm hook…). Report là **đề xuất** — mọi action phải qua `propose` → gate, không tự thực hiện.
## Triggers
- `/orca-eval [N]` — N = số session gần nhất cần quét (vd 5/10/15)
- Không có N → chỉ quét session hiện tại
- User nói "đánh giá session", "rút best practice", "tối ưu quy trình từ lịch sử"
## Input
| Tham số | Ý nghĩa | Default |
|---------|---------|---------|
| `N` | Số session gần nhất (theo mtime của `*.jsonl`) | session hiện tại |
Session logs nằm tại `~/.claude/projects/<project-slug>/*.jsonl` — slug là cwd với `/` thay bằng `-` (vd `-Users-giatran-orca-workspaces-setup-evaluation`).
## Steps
1. **Scan** — chạy `skills/orca-eval/assets/orca-eval-scan.sh [N]` từ repo root. Script trả về digest gọn (user prompts, tool errors, lệnh Bash lặp lại). KHÔNG nạp nguyên transcript — 1 session có thể hàng trăm KB.
2. **Distill** — từ digest, tìm 4 loại tín hiệu:
- **Correction**: user sửa lời agent ("không, dùng X", "sai rồi", "actually…") → quy tắc ứng xử mới
- **Repetition**: chuỗi lệnh/thao tác lặp ≥ 3 lần qua các session → ứng viên skill-hóa
- **Friction**: tool error lặp lại, permission prompt nhiều lần, retry loop → ứng viên hook/permission/config
- **Win**: workflow user approve nhanh, không phải sửa → ghi nhận best practice giữ nguyên
3. **Report** — ghi `llmwiki/wiki/draft/orca/DDMMYY-eval-report.md` theo template dưới. Cập nhật `wiki/index.md` + `wiki/log.md`.
4. **Action proposal** — mỗi finding gắn đúng 1 action: `promote-to-skill` / `update-CLAUDE.md` / `add-hook` / `keep` / `ignore`. Hiển thị bảng action cho user. **DỪNG** — action chỉ thực hiện sau khi user duyệt (qua `propose` nếu là thay đổi code/skill).
## Report template
```markdown
# DDMMYY-eval-report
**Type:** draft
**Status:** proposed
**Tags:** orca-eval, eval-report
**Proposed:** YYYY-MM-DD
**Scope:** <N session / session hiện tại> — <danh sách file jsonl đã quét>
## Best practices
| # | Tín hiệu | Loại | Bằng chứng (session, trích dẫn ngắn) |
|---|----------|------|--------------------------------------|
## Đề xuất action
| # | Finding | Action | Lý do |
|---|---------|--------|-------|
## Origin
- **Sessions:** <paths đã quét>
- **Generated by:** /orca-eval
```
## Giới hạn
- Read-only với session logs — không sửa/xóa `*.jsonl`.
- Format JSONL của Claude Code không có spec công khai — scanner fail thì fallback session hiện tại và báo rõ, không sinh report rỗng.
- Auto-trigger mỗi 30 session: **Phase 2** (cần Stop-hook + counter), chưa nằm trong skill này.
## References (community — claimed 2026-06-11)
| Repo | Áp dụng được gì |
|------|-----------------|
| [BayramAnnakov/claude-reflect](https://github.com/BayramAnnakov/claude-reflect) | Regex bắt correction ("no, use X", "remember:"), queue → review 2 giai đoạn, routing global vs project CLAUDE.md, `/reflect-skills` tìm pattern lặp để skill-hóa |
| [haddock-development/claude-reflect-system](https://github.com/haddock-development/claude-reflect-system) | Stop-hook auto-run cuối session (mẫu cho Phase 2 auto-trigger 30 session), cross-skill best-practice extraction |
| [netresearch/claude-coach-plugin](https://github.com/netresearch/claude-coach-plugin) | Friction detection — đếm permission prompts / retry loops làm tín hiệu cải tiến |
| [robonuggets/skills — calibrate](https://github.com/robonuggets/skills) | In-session self-improvement: review conversation hiện tại, đề xuất sửa skill/memory có chủ đích |
| [a-c-m reflection.md gist](https://gist.github.com/a-c-m/f4cead5ca125d2eaad073dfd71efbcfc) | Bản tối giản 1 file — giữ skill này gọn, không over-engineer |
## Origin
- **Raw:** `llmwiki/raw/evalution-engineering.md`
- **Draft:** `wiki/draft/orca/110626-orca-eval-skill.md`