SLOPSHOPPER

you-should-know-th

เตือนทิปเด็ดภาษาไทยตามสิ่งที่คุณกำลังทำ เช่น ใช้ @ mention ไฟล์, ใช้ Plan Mode, context ใกล้เต็ม

newguardcommandtoastprompt
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · you-should-know-th
› fix the failing auth test and add an audit log call ╭────────────────────────────────────────────╮ │ you-should-know-th │ ⏺ Read(src/auth.ts) │ 👋 ยินดีต้อนรับ! 3 ทิปเริ่มต้น 1. พิมพ์ @ │ ⎿ Read 6 lines │ เพื่อเลือกไฟล์ 2. พิมพ์ / เพื่อดูคำสั่ง 3. │ ⏺ Update(src/auth.ts) │ กด Esc เพื่อหยุด Claude 👉 /tips │ ⎿ Added 2 lines, removed 1 line ╰────────────────────────────────────────────╯ ⏺ Bash(bun test) ╭────────────────────────────────────────────╮ ⎿ 3 pass, 1 fail │ you-should-know-th │ │ ⚠️ ระวังคำสั่ง Bash │ ● Done. refresh now rejects expired claims and logs an audit event. │ นะครับ คำสั่งนี้อาจลบหรือเขียนทับของจริง │ │ ถ้ามี --dry-run ให้ลองก่อน │ ✻ Worked for 42s · done 4:20 PM ╰────────────────────────────────────────────╯ › /tips ⎿ you-should-know-th: 📚 คลังทิป (Mod 03) - แสดงไปแล้ว 2/9 ⎿ you-should-know-th: ---------------------------------------- ⎿ you-should-know-th: ⬜ mention-file 💡 ใช้ @ เพื่อ mention ไฟล์ได้ ⎿ you-should-know-th: ⬜ clear-session 💡 Session ยาวแล้ว ลองสรุปบริบท ⎿ you-should-know-th: ⬜ plan-mode 💡 ลองใช้ Plan Mode ไหม? ⎿ you-should-know-th: ⬜ edit-vs-write 💡 Edit ปลอดภัยกว่า Write ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

Mod 03: You Should Know TH

เด้งทิปภาษาไทยตามสิ่งที่คุณกำลังทำ เช่น พิมพ์ path ยาวๆ จะแนะนำให้ใช้ @, ใช้ Write ทับไฟล์เดิมจะแนะนำ Edit, tool พลาดสองครั้งติดจะแนะนำ Plan Mode, context เกิน 80% จะแนะนำ /compact แต่ละทิปแสดงครั้งเดียวต่อ session

คำสั่ง

คำสั่งทำอะไร
/tipsดูคลังทิปและสถานะ
/tips randomสุ่มทิป
/tips resetให้แสดงทิปใหม่ทั้งหมด
/tips <id>ดูทิปนั้น เช่น /tips plan-mode

ติดตั้ง

/plugin install you-should-know-th --marketplace tksoft11/claude-code-mods-th

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

โค้ดสอนอะไร

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

  1. ทำ Mod ที่ "สอน" ผู้ใช้ (ไม่บล็อกอะไร)
  2. อ่าน prompt ด้วย prompt.submit
  3. แยกชนิด tool ด้วย e.tool === "Write" แล้ว TypeScript รู้ field ให้เอง
  4. ดูผลหลัง next(e) (isError)
  5. อ่าน context ด้วย $.session.usage()

หมายเหตุ

เพิ่มทิปของทีมเองได้ที่ TIPS ด้านบนของไฟล์

Source 1 files
hooks/register.tsx 223 lines
1/**
2 * ============================================================================
3 * MOD 03: You Should Know TH - ตัวเตือนทิปเด็ด (ฉบับสอนทำ)
4 * ============================================================================
5 * แรงบันดาลใจจาก: ปลั๊กอิน "you-should-know" ที่ Anthropic ทำเป็นตัวอย่าง
6 *
7 * หน้าที่ของ Mod นี้:
8 * 1. เตือนทิปตามบริบท (contextual tips) ด้วย toast มุมจอ
9 * 2. ไม่บล็อกอะไรเลย แค่ "สอน" ผู้ใช้ระหว่างทำงาน (Educational Mod)
10 * 3. จำว่าแสดงทิปไหนไปแล้วด้วย Set จะได้ไม่สแปม
11 * 4. มีคำสั่ง /tips สำหรับดู สุ่ม และรีเซ็ตทิป
12 */
13
14import type { EngineInterface, Register } from 'claude-code'
15
16interface Tip {
17  id: string
18  trigger: string // จะแสดงเมื่อไหร่ (ไว้อธิบายใน /tips)
19  title: string
20  message: string
21  learnMore: string // คำสั่งหรือสิ่งที่ให้ลอง
22}
23
24// ==========================================================================
25// บทที่ 1: คลังทิป - เพิ่มทิปของทีมคุณเองได้ตรงนี้
26// ==========================================================================
27const TIPS: Tip[] = [
28  {
29    id: 'mention-file',
30    trigger: 'พิมพ์ path ของไฟล์ยาวๆ ใน prompt',
31    title: '💡 ใช้ @ เพื่อ mention ไฟล์ได้',
32    message: "แทนที่จะพิมพ์ path เต็มๆ ให้พิมพ์ '@Button.tsx' แล้วเลือกไฟล์ Claude จะเห็นไฟล์นั้นทันที",
33    learnMore: 'ลองพิมพ์ @ แล้วเลือกไฟล์',
34  },
35  {
36    id: 'clear-session',
37    trigger: 'เรียก tool เกิน 30 ครั้ง',
38    title: '💡 Session ยาวแล้ว ลองสรุปบริบท',
39    message: 'บทสนทนายาวทำให้แต่ละรอบใช้ token มากขึ้น ถ้าเปลี่ยนเรื่องให้ /clear ถ้ายังทำเรื่องเดิมให้ /compact',
40    learnMore: '/compact หรือ /clear',
41  },
42  {
43    id: 'plan-mode',
44    trigger: 'tool ล้มเหลวติดกัน 2 ครั้ง',
45    title: '💡 ลองใช้ Plan Mode ไหม?',
46    message: 'งานที่ซับซ้อนควรให้ Claude วางแผนก่อนลงมือ กด Shift+Tab จนขึ้น plan mode แล้ว Claude จะเสนอแผนให้ดูก่อน',
47    learnMore: 'กด Shift+Tab',
48  },
49  {
50    id: 'edit-vs-write',
51    trigger: 'ใช้ Write ทับไฟล์ที่มีอยู่แล้ว',
52    title: '💡 Edit ปลอดภัยกว่า Write',
53    message: 'Edit แก้เฉพาะจุด ส่วน Write เขียนทับทั้งไฟล์ Write จึงเหมาะกับไฟล์ใหม่มากกว่า',
54    learnMore: "บอก Claude ว่า 'แก้เฉพาะจุดด้วย Edit นะ'",
55  },
56  {
57    id: 'bash-check',
58    trigger: 'รันคำสั่ง Bash ที่อาจอันตราย (rm -rf, sudo, --force)',
59    title: '⚠️ ระวังคำสั่ง Bash นะครับ',
60    message: 'คำสั่งนี้อาจลบหรือเขียนทับของจริง ถ้ามี --dry-run ให้ลองก่อน และอ่านคำสั่งก่อนกดอนุญาต',
61    learnMore: 'เช่น git clean -n ก่อน git clean -f',
62  },
63  {
64    id: 'thai-comment',
65    trigger: 'สุ่มระหว่างทำงาน',
66    title: '🇹🇭 ขอคอมเมนต์ภาษาไทยได้',
67    message: "บอก Claude ว่า 'ใส่คอมเมนต์ภาษาไทยด้วย' ได้เลย เหมาะกับทีมที่ส่งงานให้คนไทยอ่าน",
68    learnMore: "ลอง: 'อธิบายฟังก์ชันนี้เป็นภาษาไทย'",
69  },
70  {
71    id: 'model-choice',
72    trigger: 'สุ่มระหว่างทำงาน',
73    title: '💰 เลือก model ให้เหมาะกับงาน',
74    message: 'งานเล็กๆ เช่น แปลภาษาหรือสรุปไฟล์สั้นๆ ใช้ model ที่เล็กกว่าได้ ถูกและเร็วกว่า',
75    learnMore: '/model',
76  },
77  {
78    id: 'context-usage',
79    trigger: 'context ใช้ไปเกิน 80%',
80    title: '🧠 Context ใกล้เต็มแล้ว!',
81    message: 'ควร /compact ตอนนี้ ก่อนที่ระบบจะสรุปให้เองกลางงาน',
82    learnMore: '/compact หรือ /context เพื่อดูรายละเอียด',
83  },
84  {
85    id: 'first-time',
86    trigger: 'ใช้ Mod นี้ครั้งแรก',
87    title: '👋 ยินดีต้อนรับ! 3 ทิปเริ่มต้น',
88    message: '1. พิมพ์ @ เพื่อเลือกไฟล์  2. พิมพ์ / เพื่อดูคำสั่ง  3. กด Esc เพื่อหยุด Claude',
89    learnMore: '/tips เพื่อดูทิปทั้งหมด',
90  },
91]
92
93// ==========================================================================
94// บทที่ 2: state ของ Mod - ตัวแปรนอก register() จำค่าข้าม event ได้
95// ==========================================================================
96// (ตัวแปรพวกนี้จะเริ่มใหม่เมื่อ mod ถูก reload ซึ่งสำหรับทิปถือว่าโอเค)
97const shownTips = new Set<string>() // แสดงไปแล้ว -> ไม่แสดงซ้ำ
98let toolCallCount = 0
99let failStreak = 0 // ล้มเหลวติดกันกี่ครั้ง
100
101// แสดงทิปด้วย toast (ถ้ายังไม่เคยแสดง) แล้วจดไว้ใน Set
102function showTip($: EngineInterface, id: string): void {
103  const tip = TIPS.find(t => t.id === id)
104  if (!tip || shownTips.has(id)) return
105
106  $.ui.toast(`${tip.title}\n${tip.message}\n👉 ${tip.learnMore}`, { timeoutMs: 10_000 })
107  shownTips.add(id)
108}
109
110function formatTip(tip: Tip): string {
111  return `${tip.title}\nแสดงเมื่อ: ${tip.trigger}\n${tip.message}\n👉 ${tip.learnMore}`
112}
113
114export const register: Register = on => {
115  on('session.start', async ($, e, next) => {
116    toolCallCount = 0
117    failStreak = 0
118    shownTips.clear()
119
120    await $.command.register({ name: 'tips', description: 'ดูทิปทั้งหมด (/tips random, /tips reset, /tips <id>)', argumentHint: '[list|random|reset|id]' })
121
122    // ทิปต้อนรับแสดงแค่ครั้งแรกที่ติดตั้ง: ใช้ $.store จำข้าม session
123    if ((await $.store.get('welcomed')) !== true) {
124      showTip($, 'first-time')
125      await $.store.set('welcomed', true)
126    }
127    return next(e)
128  })
129
130  // ==========================================================================
131  // บทที่ 3: prompt.submit - ดูว่าผู้ใช้พิมพ์อะไร (แค่อ่าน ไม่แก้)
132  // ==========================================================================
133  on('prompt.submit', ($, e, next) => {
134    // มี path ยาวๆ เช่น src/components/Button.tsx แต่ไม่ได้ใช้ @
135    if (/[\w.-]+\/[\w./-]+\.\w{1,5}\b/.test(e.text) && !e.text.includes('@')) {
136      showTip($, 'mention-file')
137    }
138    return next(e)
139  })
140
141  // ==========================================================================
142  // บทที่ 4: tool.call - เช็ค pattern ก่อนรัน + ดูผลหลังรัน
143  // ==========================================================================
144  on('tool.call', async ($, e, next) => {
145    toolCallCount++
146
147    // เช็คแบบ e.tool === "..." แล้ว TypeScript จะรู้ว่า e มี field อะไร
148    if (e.tool === 'Write' && (await $.fs.exists(e.file_path))) {
149      showTip($, 'edit-vs-write')
150    }
151    if (e.tool === 'Bash' && /rm -rf|sudo |--force/.test(e.command)) {
152      showTip($, 'bash-check')
153    }
154    if (toolCallCount === 31) {
155      showTip($, 'clear-session')
156    } else if (toolCallCount % 15 === 0) {
157      // ทุก 15 ครั้ง สุ่มทิปที่ยังไม่เคยเห็น
158      const notShown = TIPS.filter(t => !shownTips.has(t.id))
159      const pick = notShown[Math.floor(Math.random() * notShown.length)]
160      if (pick) showTip($, pick.id)
161    }
162
163    const ran = await next(e) // ให้ tool รันจริง
164
165    // ผลออกมาเป็น error -> นับ streak, ถ้าพลาด 2 ครั้งติดแนะนำ Plan Mode
166    failStreak = ran.isError ? failStreak + 1 : 0
167    if (failStreak >= 2) showTip($, 'plan-mode')
168
169    return ran
170  })
171
172  // ==========================================================================
173  // บทที่ 5: turn.complete - เช็ค context หลังจบแต่ละรอบ
174  // ==========================================================================
175  on('turn.complete', async ($, e, next) => {
176    const { context } = await $.session.usage()
177    if ((context.percent ?? 0) > 80) showTip($, 'context-usage')
178    return next(e)
179  })
180
181  // ==========================================================================
182  // บทที่ 6: คำสั่งแบบมี subcommand - /tips random, /tips reset
183  // ==========================================================================
184  on('command.run', { command: 'tips' }, async ($, e) => {
185    const arg = e.args.trim()
186
187    if (arg === '' || arg === 'list') {
188      const rows = TIPS.map(t => `${shownTips.has(t.id) ? '✅' : '⬜'} ${t.id.padEnd(16)} ${t.title}`)
189      return {
190        text: [
191          `📚 คลังทิป (Mod 03) - แสดงไปแล้ว ${shownTips.size}/${TIPS.length}`,
192          '----------------------------------------',
193          ...rows,
194          '',
195          '/tips random = สุ่มทิป | /tips reset = รีเซ็ต | /tips <id> = ดูทิปนั้น',
196        ].join('\n'),
197      }
198    }
199    if (arg === 'random') {
200      const tip = TIPS[Math.floor(Math.random() * TIPS.length)]
201      return { text: tip ? formatTip(tip) : 'ยังไม่มีทิป' }
202    }
203    if (arg === 'reset') {
204      shownTips.clear()
205      await $.store.delete('welcomed')
206      return { text: '🔄 รีเซ็ตแล้ว ทิปทั้งหมดจะแสดงใหม่' }
207    }
208
209    const found = TIPS.find(t => t.id === arg)
210    return { text: found ? formatTip(found) : `❌ ไม่เจอทิป "${arg}" ลอง /tips list` }
211  })
212}
213
214/**
215 * ========================================================================
216 * การบ้าน / ต่อยอด
217 * ========================================================================
218 * 1. เพิ่มทิปกฎของบริษัท เช่น "ห้าม push เข้า main ตรงๆ" แล้วเช็คใน tool.call
219 *    ว่า e.tool === "Bash" && e.command.includes("git push origin main")
220 * 2. เก็บ shownTips ลง $.store เพื่อจำข้าม session
221 * 3. ใช้ $.model.complete สร้างทิปใหม่จากสิ่งที่ผู้ใช้ทำบ่อย
222 */
223