SLOPSHOPPER

pinboard

/pin 으로 결정 사항과 할 일을 고정해 매 요청에 싣는다

newcommandprompt
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · pinboard
› 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 › /pin ⎿ pinboard: 사용법: /pin <결정 내용> 또는 /pin 할일: <내용> ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

usage-hud

A Claude Code mod that draws a usage HUD above the prompt. Two ASCII dogs keep an eye on your limits.

Claude Code 프롬프트 위에 사용량 HUD를 그려주는 mod입니다. 강아지 두 마리가 한도를 지켜봅니다.

    __                5h ████░░░░ 13%       / \__              7d █████░░░ 64%      ctx ██░░░░░░ 23% 226k/1.0M
 o-''|\_____/)          ↻ 40m                 (    @\___           ↻ 17h40m           last 1m54s · tools 13   +$0.80 2.2M↑ 10k↓
  \_/|_)     )          “공 물어올게”          /         O          “장부 정리 중”      cache 99%
     \  __  /                                /   (_____/                              Opus 5.5 · medium   AUTO   $7.06   ⎇ main ✓
     (_/ (_/                                 /_____/   U

설치

Claude Code 터미널 세션의 프롬프트에서 다음을 입력합니다.

/plugin install usage-hud --marketplace Jang-seungminn/usage-hud

Add marketplace?에 y를 입력하고, scope를 고릅니다. 모든 세션에서 쓰려면 user scope를 선택합니다. 설치한 세션에서 바로 동작합니다.

같은 marketplace에 다른 mod도 있습니다. 필요한 것만 같은 방식으로 설치합니다.

/plugin install plan-board --marketplace Jang-seungminn/usage-hud
mod하는 일
plan-board사이드 패널: 목표, superpowers 플랜 진행률, 작업, 서브에이전트, 핀, 다음 단계, 인수인계
auto-handoff/handoff로 대화를 .claude/handoff/HANDOFF.md에 정리, 새 세션에서 /pickup으로 이어받기, /note로 메모. 컨텍스트 70%·90%에서 알림
pinboard/pin <결정>, /pin 할일: <내용>, /unpin <번호>, /pins. 핀은 .claude/pinboard.md에 저장되고 매 요청의 시스템 프롬프트에 실림
next-steps작업을 끝낸 응답 끝에 다음 단계 체크박스 2~3개를 제안. /next로 목록, /next <번호>로 입력창에 넣기
prompt-coach/coach <요청>(또는 입력창의 내용)을 대상·완료 기준·제약이 분명한 요청으로 다듬어 입력창에 넣기. 실행 전 불명확하면 질문하라는 규칙 포함
korean-ui한국어 응답 규칙, 기본 슬래시 명령 설명을 한국어로

auto-handoff, pinboard, next-steps, prompt-coach, korean-ui는 시스템 프롬프트에 짧은 규칙을 하나씩 덧붙입니다. 쓰지 않는 것은 설치하지 않는 편이 요청당 토큰을 아낍니다.

보여주는 것

항목내용
5h / 7d사용 한도 게이지, 리셋까지 남은 시간, 리셋 전에 한도가 바닥날 것 같으면 ⚡N 후 소진
ctxcontext window 사용량
턴진행 중에는 경과 시간과 도구 호출 수, 끝난 뒤에는 그 턴의 비용과 입출력 토큰
cache마지막 응답의 prompt cache 적중률
세션모델 · effort, 권한 모드 배지, 누적 비용
git브랜치, ahead/behind, staged/수정/추적 안 된 파일 수
agents / bg실행 중인 서브에이전트와 백그라운드 작업 수

알림(toast):

  • context가 80%를 넘거나 사용 한도가 90%를 넘는 순간
  • 50% 넘게 쓴 한도가 리셋될 때

강아지

  • 5h 강아지(전신): 5시간 한도와 context를 지켜봅니다.
  • 7d 강아지(얼굴): 7일 한도를 지켜봅니다.

수치에 따라 상태가 바뀝니다.

상태조건
여유기본
작업 중Claude가 일하는 동안
완료턴이 끝나고 20초 동안
진땀50% 이상
패닉80% 이상
소진100%
수면10분 넘게 쉬는 중

/hud를 입력하면 강아지를 끄고 켭니다. 강아지는 band가 5줄을 쓸 수 있을 때만 나오고, 터미널이 130칸 이상이면 모든 정보가 한 줄로 나란히 배치됩니다.

참고

  • 사용 한도(5h/7d)는 Claude 구독 계정에서만 표시됩니다.
  • 권한 모드 배지는 다음 프롬프트나 도구 실행 때 갱신됩니다.

plan-board

사이드 패널(fullscreen에서는 transcript 옆, 아니면 프롬프트 위)에 이 세션이 무엇을 하려는지와 어디까지 왔는지를 보여줍니다. 보여줄 것이 처음 생길 때 패널이 한 번 열리고, 닫은 뒤에는 /board로 다시 엽니다.

◎ 목표
GitHub 로그인 붙이기
plan Users can sign in with GitHub.
/goal bun test exits 0

▤ 플랜 2026-10-11-auth.md
plan  Auth Feature Implementation Plan
task  ██████░░░░░░ 1/3
step  ██████████░░ 3/5
▶ Task 2: Session cookie
  ✓ Step 1: Write the failing test
  ○ Step 2: Implement setCookie
○ Task 3: Logout

☰ 작업 ████░░░░ 1/2
▶ Writing plan
✓ Write spec

⚙ 에이전트 실행 1 · 완료 2
● Explore auth code [Explore] 1m05s
✓ Review spec 42s
영역어디서 오나
목표/mission <목표>로 정한 목표. 없으면 세션의 첫 프롬프트. 플랜 파일의 Goal: 줄, 내장 /goal(또는 ProposeGoal)의 완료 조건도 함께
플랜…/plans/*.md 파일을 Write/Edit 하면 읽어서, 체크박스가 있는 제목(### Task N: …)을 작업으로, 체크박스를 단계로 셉니다. 매 턴이 끝날 때 다시 읽으므로 sed 등으로 체크해도 반영됩니다. superpowers의 docs/superpowers/plans/, docs/plans/ 모두 해당
spec…/specs/*.md 또는 *-design.md를 쓰면 제목을 표시
작업 / 할 일TaskCreate·TaskUpdate·TaskList, TodoWrite. 진행 중 → 대기 → 완료 순으로, 완료는 최근 3개만
에이전트Agent 호출과 SubagentStart/Stop. 실행 중인 것은 경과 시간, 끝난 것은 최근 3개
핀pinboard의 .claude/pinboard.md (10초마다, 턴이 끝날 때 다시 읽음)
다음 단계마지막 응답의 다음 단계 아래 체크박스 (next-steps가 제안하게 함)
인수인계auto-handoff의 .claude/handoff/HANDOFF.md 작성 시각과 NOTES.md 메모 수

핀·다음 단계·인수인계는 해당 mod가 없어도 파일이나 응답 형식만 맞으면 표시됩니다.

명령:

  • /board 패널 열기, /board clear 작업·플랜·에이전트 목록 비우기
  • /mission <목표> 목표 정하기, /mission 현재 목표 보기, /mission clear 지우기

개발

각 mod는 plugins/<이름>/에 있고, 저장소 루트는 marketplace입니다.

claude --plugin-dir ./plugins/usage-hud --plugin-dir ./plugins/plan-board
claude plugin validate .                     # marketplace
for m in plugins/*; do claude plugin validate $m && claude plugin test $m; done

License

MIT

Source 2 files
hooks/register.tsx 113 lines
1import { atom, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4import type { Pin } from '../types'
5
6// plan-board 패널이 .claude/pinboard.md 를 읽어 핀을 보여준다. 프롬프트 위 영역은 usage-hud 가 쓴다.
7const pins = atom({ plugin: 'pinboard', key: 'pins' } as const, [])
8const LINE = /^- \[(결정|할일)\] (\d{4}-\d{2}-\d{2}) (.+)$/
9const HEAD = '# Pinboard\n\n/pin, /unpin 으로 관리한다. 한 줄에 핀 하나.\n\n'
10
11const RULES = `# Pinboard
12- 아래 핀은 사용자가 고정한 결정 사항과 진행 중인 할 일이다. 요청이 핀의 결정과 충돌하면 진행하기 전에 해당 핀을 인용해 알린다.
13- 사용자가 무언가를 확정하면("이걸로 가자", "결정" 등) \`/pin <내용>\`으로 기록할지 한 줄로 제안한다. 핀 파일을 직접 고치지 않는다.`
14
15export const parse = (text: string): Pin[] =>
16  text.split('\n').flatMap(line => {
17    const found = LINE.exec(line.trim())
18
19    return found === null
20      ? []
21      : [{ kind: found[1] as Pin['kind'], date: found[2] ?? '', text: found[3] ?? '' }]
22  })
23
24export const format = (list: readonly Pin[]): string =>
25  HEAD + list.map(pin => `- [${pin.kind}] ${pin.date} ${pin.text}`).join('\n') + '\n'
26
27const file = async ($: EngineInterface) => `${await $.session.root()}/.claude/pinboard.md`
28
29const load = async ($: EngineInterface): Promise<Pin[]> => {
30  try {
31    return parse(await $.fs.read(await file($)))
32  } catch {
33    return []
34  }
35}
36
37const save = async ($: EngineInterface, list: Pin[]) => {
38  await $.fs.write(await file($), format(list))
39  await update($, pins, () => list)
40}
41
42// 한국 시간 기준 날짜
43const today = async ($: EngineInterface) =>
44  new Date((await $.clock.now()) + 9 * 3_600_000).toISOString().slice(0, 10)
45
46const listing = (list: readonly Pin[]) =>
47  list.map((pin, i) => `${i + 1}. [${pin.kind}] ${pin.text} (${pin.date})`).join('\n')
48
49export const register: Register = on => {
50  on('session.start', async ($, e, next) => {
51    await $.command.register({
52      name: 'pin',
53      description: '결정 사항을 고정한다. "할일:"로 시작하면 진행 중 할 일로 고정',
54      argumentHint: '[할일:] <내용>',
55    })
56    await $.command.register({
57      name: 'unpin',
58      description: '번호의 핀을 내린다 (끝난 할 일, 바뀐 결정)',
59      argumentHint: '<번호>',
60    })
61    await $.command.register({ name: 'pins', description: '고정된 핀 목록 보기' })
62    await update($, pins, await load($).then(list => () => list))
63
64    return next(e)
65  })
66
67  on('command.run', { command: 'pin' }, async ($, e) => {
68    const raw = e.args.trim()
69    const isTodo = /^(할일|todo)\s*[::]/i.test(raw)
70    const text = raw.replace(/^(할일|todo|결정)\s*[::]\s*/i, '').replace(/\s+/g, ' ')
71
72    if (text === '') {
73      return { text: '사용법: /pin <결정 내용>  또는  /pin 할일: <내용>' }
74    }
75
76    const list = [...(await load($)), { kind: isTodo ? '할일' : '결정', date: await today($), text } as Pin]
77    await save($, list)
78
79    return { text: `고정했습니다.\n${listing(list)}` }
80  })
81
82  on('command.run', { command: 'unpin' }, async ($, e) => {
83    const list = await load($)
84    const at = Number(e.args.trim()) - 1
85    const gone = list[at]
86
87    if (gone === undefined) {
88      return { text: `사용법: /unpin <번호>\n${listing(list) || '고정된 핀이 없습니다.'}` }
89    }
90
91    await save($, list.filter((_, i) => i !== at))
92
93    return { text: `내렸습니다: [${gone.kind}] ${gone.text}` }
94  })
95
96  on('command.run', { command: 'pins' }, async $ => {
97    const list = await load($)
98    await update($, pins, () => list)
99
100    return { text: listing(list) || '고정된 핀이 없습니다. /pin <내용>으로 추가하세요.' }
101  })
102
103  on('prompt.compose', async ($, e, next) => {
104    const composed = await next(e)
105    const list = await load($)
106    const text = `${RULES}\n\n현재 핀:\n${listing(list) || '(없음)'}`
107
108    return {
109      sections: [...composed.sections, { id: 'pinboard:pins', text, scope: 'session' }],
110    }
111  })
112}
113
types/index.d.ts 8 lines
1export type Pin = { kind: '결정' | '할일'; date: string; text: string }
2
3declare module 'claude-code' {
4  interface PluginState {
5    pinboard: { pins: Pin[] }
6  }
7}
8