SLOPSHOPPER

reflect-th

ทบทวนงานแบบ Retrospective: สรุปว่า Claude ใช้ tool อะไร สำเร็จ/ล้มเหลวเท่าไหร่ พร้อมคำแนะนำและบันทึกเป็นไฟล์

newguardcommandtoastprompt
★ 3v1.0.0MITupdated 2026-10-23tksoft11/claude-code-mods-th/mods/mod-07-reflect
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · reflect-th
› fix the failing auth test and add an audit log call ⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Update(src/auth.ts) ⎿ Added 2 lines, removed 1 line ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM › /reflect ⎿ reflect-th: 🪞 Reflect Report - ทบทวนงาน (Mod 07) ⎿ reflect-th: ======================================== ⎿ reflect-th: 📝 งาน: fix the failing auth test and add an audit log call ⎿ reflect-th: ⏱️ ใช้เวลา: 0วิ ⎿ reflect-th: 🔧 เรียก tool: 9 ครั้ง (สำเร็จ 8, ล้มเหลว 1) - 88.9% ⎿ reflect-th: 📁 แตะไฟล์: 4 ไฟล์ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

Mod 07: Reflect TH

เก็บ work log ของทุก tool ที่ Claude เรียก แล้วสรุปเป็นรายงานทบทวนงาน: ใช้เวลาเท่าไหร่ สำเร็จกี่เปอร์เซ็นต์ แตะกี่ไฟล์ ใช้ tool ไหนเยอะสุด พร้อมคำแนะนำอัตโนมัติและคำถามทบทวน ถ้า tool ล้มเหลว 3 ใน 5 ครั้งล่าสุดจะเด้งเตือน

คำสั่ง

คำสั่งทำอะไร
/reflectดูรายงาน
/reflect statsสถิติสั้นๆ
/reflect feedback <ข้อความ>จดบทเรียน (จำข้าม session)
/reflect saveบันทึกเป็น REFLECT-YYYY-MM-DD.md

ติดตั้ง

/plugin install reflect-th --marketplace tksoft11/claude-code-mods-th

ต้องตั้ง CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 ก่อนเปิด claude (ดู README หลัก)

โค้ดสอนอะไร

เปิด hooks/register.tsx แล้วอ่านตามบทที่เขียนไว้ในคอมเมนต์:

  1. เก็บ log ก่อน next แล้วอัปเดตผลหลัง next
  2. วิเคราะห์ log ด้วย Map/Set
  3. จำ feedback ด้วย $.store
  4. เขียนไฟล์ด้วย $.fs.write

หมายเหตุ

work log อยู่ในหน่วยความจำ จะเริ่มใหม่เมื่อเปิด session ใหม่หรือ Mod ถูกโหลดใหม่

Source 1 files
hooks/register.tsx 193 lines
1/**
2 * ============================================================================
3 * MOD 07: Reflect TH - ทบทวนตัวเองหลังจบงาน แบบ Scrum Retrospective (ฉบับสอนทำ)
4 * ============================================================================
5 * แรงบันดาลใจจาก: https://github.com/BayramAnnakov/claude-reflect
6 *
7 * หน้าที่ของ Mod นี้:
8 * 1. เก็บ Work Log ทุกครั้งที่ Claude เรียก tool (tool อะไร, แตะไฟล์ไหน, สำเร็จไหม)
9 * 2. ถ้าล้มเหลวติดๆ กัน เด้งเตือนว่าอาจต้องเปลี่ยนวิธี
10 * 3. /reflect สรุปงาน + วิเคราะห์อัตโนมัติ + คำถามทบทวน
11 * 4. /reflect feedback <ข้อความ> จดบทเรียน (จำข้าม session)
12 * 5. /reflect save บันทึกรายงานเป็นไฟล์ REFLECT-YYYY-MM-DD.md
13 */
14
15import type { EngineInterface, Register } from 'claude-code'
16
17interface WorkLog {
18  time: number
19  tool: string
20  target: string // ไฟล์หรือคำสั่งที่เกี่ยวข้อง
21  ok: boolean
22}
23
24interface Feedback {
25  time: number
26  text: string
27}
28
29// ==========================================================================
30// บทที่ 1: state ของ Mod (เริ่มใหม่ทุก session)
31// ==========================================================================
32let workLogs: WorkLog[] = []
33let task = '' // prompt แรกของ session = "งาน" ที่ทำ
34let startedAt = 0
35let projectRoot = ''
36let hasWarnedStreak = false
37
38// สรุปสั้นๆ ว่า tool นี้ทำอะไร
39function targetOf(e: { tool: string } & Record<string, unknown>): string {
40  for (const key of ['file_path', 'notebook_path', 'command', 'url', 'pattern', 'description']) {
41    const value = e[key]
42    if (typeof value === 'string' && value) return value.length > 70 ? value.slice(0, 67) + '...' : value
43  }
44  return ''
45}
46
47function formatDuration(ms: number): string {
48  const sec = Math.floor(ms / 1000)
49  return sec < 60 ? `${sec}วิ` : `${Math.floor(sec / 60)}นาที ${sec % 60}วิ`
50}
51
52function timeAgo(ms: number): string {
53  const sec = Math.floor(ms / 1000)
54  return sec < 60 ? `${sec}วิที่แล้ว` : `${Math.floor(sec / 60)}นาทีที่แล้ว`
55}
56
57async function readFeedbacks($: EngineInterface): Promise<Feedback[]> {
58  const saved = await $.store.get('feedbacks')
59  return Array.isArray(saved) ? (saved as Feedback[]) : []
60}
61
62// ==========================================================================
63// บทที่ 2: วิเคราะห์อัตโนมัติ - หัวใจของ Reflect
64// ==========================================================================
65function buildReport(now: number, feedbacks: Feedback[]): string {
66  const total = workLogs.length
67  const success = workLogs.filter(w => w.ok).length
68  const failed = total - success
69  const rate = total === 0 ? 0 : (success / total) * 100
70  const files = new Set(workLogs.filter(w => /[/\\.]/.test(w.target) && w.tool !== 'Bash').map(w => w.target))
71
72  // นับว่าใช้ tool ไหนบ่อยสุด
73  const toolCount = new Map<string, number>()
74  for (const w of workLogs) toolCount.set(w.tool, (toolCount.get(w.tool) ?? 0) + 1)
75  const [topTool, topCount] = [...toolCount.entries()].sort((a, b) => b[1] - a[1])[0] ?? ['-', 0]
76
77  const lines = [
78    '🪞 Reflect Report - ทบทวนงาน (Mod 07)',
79    '========================================',
80    `📝 งาน: ${task || '(ยังไม่มี prompt)'}`,
81    `⏱️ ใช้เวลา: ${formatDuration(now - startedAt)}`,
82    `🔧 เรียก tool: ${total} ครั้ง (สำเร็จ ${success}, ล้มเหลว ${failed}) - ${rate.toFixed(1)}%`,
83    `📁 แตะไฟล์: ${files.size} ไฟล์`,
84    `🛠️ ใช้เยอะสุด: ${topTool} (${topCount} ครั้ง)`,
85  ]
86
87  if (total > 0) {
88    lines.push('', '📊 ล่าสุด 8 รายการ:')
89    for (const w of workLogs.slice(-8).reverse()) {
90      lines.push(`  ${w.ok ? '✅' : '❌'} ${w.tool} - ${w.target || '-'} (${timeAgo(now - w.time)})`)
91    }
92  }
93
94  lines.push('', '🤔 การทบทวน (Self-Reflection):')
95  if (total === 0) lines.push('  ยังไม่มีงานให้ทบทวน')
96  else if (failed === 0) lines.push('  ✅ สำเร็จ 100% - แผนดีมาก')
97  if (failed > total * 0.3) lines.push('  ⚠️ ล้มเหลวเกิน 30% - งานถัดไปลองใช้ Plan Mode วางแผนก่อน')
98  if (total >= 5 && topCount > total * 0.6) lines.push(`  💡 ใช้ ${topTool} เกิน 60% ของทั้งหมด - อาจมีวิธีที่เร็วกว่า`)
99  if (files.size > 10) lines.push('  💡 แตะไฟล์เยอะมาก - ลองแบ่งงานเป็นชิ้นเล็กลง')
100
101  if (feedbacks.length > 0) {
102    lines.push('', '📌 บทเรียนที่จดไว้:')
103    for (const f of feedbacks.slice(-5)) lines.push(`  - ${f.text}`)
104  }
105
106  lines.push('', '💡 คำถามทบทวน:', '  1. งานนี้สำเร็จตามที่ต้องการไหม?', '  2. มีอะไรที่ทำได้ดีกว่านี้ไหม?', '  3. รอบหน้าควรบอก Claude อะไรตั้งแต่แรก?')
107  return lines.join('\n')
108}
109
110export const register: Register = on => {
111  on('session.start', async ($, e, next) => {
112    workLogs = []
113    task = ''
114    hasWarnedStreak = false
115    startedAt = await $.clock.now()
116    projectRoot = e.cwd
117    await $.command.register({ name: 'reflect', description: 'ทบทวนงาน (/reflect stats, /reflect feedback <ข้อความ>, /reflect save)', argumentHint: '[stats|feedback|save]' })
118    return next(e)
119  })
120
121  // จด prompt แรกเป็นชื่องาน (ข้ามคำสั่ง / และข้อความที่ระบบส่งเอง)
122  on('prompt.submit', ($, e, next) => {
123    if (!task && !e.command && !e.isMeta) task = e.text.split('\n')[0]?.slice(0, 100) ?? ''
124    return next(e)
125  })
126
127  // ==========================================================================
128  // บทที่ 3: เก็บ Log ทุกครั้งที่เรียก tool แล้วอัปเดตว่าสำเร็จไหม
129  // ==========================================================================
130  on('tool.call', async ($, e, next) => {
131    const log: WorkLog = { time: await $.clock.now(), tool: e.tool, target: targetOf(e as unknown as { tool: string } & Record<string, unknown>), ok: true }
132    workLogs.push(log)
133    if (workLogs.length > 500) workLogs.shift()
134
135    const ran = await next(e)
136    log.ok = ran.deny === undefined && !ran.isError
137
138    // ใน 5 ครั้งล่าสุด ล้มเหลว 3 ครั้งขึ้นไป -> เตือน (ครั้งเดียวจนกว่าจะดีขึ้น)
139    const recentFails = workLogs.slice(-5).filter(w => !w.ok).length
140    if (recentFails >= 3 && !hasWarnedStreak) {
141      $.ui.toast('🪞 ล้มเหลวหลายครั้งติดกัน อาจต้องเปลี่ยนวิธี ลองกด Esc แล้วบอก Claude ให้วางแผนใหม่')
142      hasWarnedStreak = true
143    }
144    if (recentFails === 0) hasWarnedStreak = false
145
146    return ran
147  })
148
149  // ==========================================================================
150  // บทที่ 4: Feedback Loop และคำสั่ง /reflect
151  // ==========================================================================
152  on('command.run', { command: 'reflect' }, async ($, e) => {
153    const args = e.args.trim()
154    const [sub = '', ...rest] = args.split(/\s+/)
155    const now = await $.clock.now()
156
157    if (sub === 'feedback') {
158      const text = rest.join(' ')
159      if (!text) return { text: 'วิธีใช้: /reflect feedback รอบหน้าควรรัน test ก่อน' }
160      const feedbacks = [...(await readFeedbacks($)), { time: now, text }].slice(-50)
161      await $.store.set('feedbacks', feedbacks)
162      return { text: `📌 จดบทเรียนแล้ว (${feedbacks.length} ข้อ): ${text}` }
163    }
164
165    if (sub === 'stats') {
166      const ok = workLogs.filter(w => w.ok).length
167      return { text: `🪞 ${formatDuration(now - startedAt)} | tool ${workLogs.length} ครั้ง | สำเร็จ ${ok} | ล้มเหลว ${workLogs.length - ok}` }
168    }
169
170    const report = buildReport(now, await readFeedbacks($))
171
172    if (sub === 'save') {
173      const d = new Date(now) // วันที่ตามเวลาเครื่อง (toISOString จะเป็นเวลา UTC)
174      const date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
175      const sep = projectRoot.includes('\\') ? '\\' : '/'
176      const path = `${projectRoot}${sep}REFLECT-${date}.md`
177      await $.fs.write(path, '```\n' + report + '\n```\n')
178      return { text: `💾 บันทึกแล้ว: ${path}` }
179    }
180
181    return { text: report }
182  })
183}
184
185/**
186 * ========================================================================
187 * การบ้าน / ต่อยอด
188 * ========================================================================
189 * 1. เก็บสรุปทุก session ลง $.store แล้วดูว่าเราทำงานเร็วขึ้นไหม
190 * 2. ส่ง workLogs ให้ $.model.complete วิเคราะห์ว่า "อะไรดี/ไม่ดี" เป็นภาษาไทย
191 * 3. แชร์ REFLECT-*.md ให้ทีมดูใน retrospective
192 */
193