SLOPSHOPPER

memo-pad

Claude가 일하는 동안 다음에 시킬 일을 메모해 뒀다가 버튼 한 번으로 입력창에 넣어요

newpanespinnercommand
★ 1v1.1.0MITupdated 2026-10-06SeongGwangJu/k-mods/mods/memo-pad
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · memo-pad
│ ┃ 메모 ✕ › fix the failing auth test and add an audit log call │ ┃ 메모: 다음에 시킬 일을 적고 Enter ⏎ 추가 │ ⏺ 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 │ │ › /m │ │ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Pane · 메모
메모: 다음에 시킬 일을 적고 Enter ⏎ 추가
README

메모장

Claude가 일하는 동안 다음에 시킬 일을 적어 뒀다가, 버튼 한 번으로 입력창에 넣어요.

설치

/plugin marketplace add SeongGwangJu/k-mods
/plugin install memo-pad@k-mods

쓰는 법

  • /m으로 메모장을 열고 닫아요. Claude가 한창 작업 중이어도 바로 열려요.
  • 입력창에 메모를 적고 Enter. 바로 목록에 추가돼요.
  • 메모 옆 [넣기]를 누르면 그 메모가 프롬프트 입력창에 채워져요(전송은 직접 Enter를 눌러야 해요).
  • [x]를 누르면 그 메모만 지워져요.
  • 메모가 하나라도 있으면 프롬프트 아래 힌트 줄 끝에 ✎ 메모 N (/m)이 붙어서 잊지 않게 해줘요.
  • 메모는 작업 폴더(디렉터리)별로 따로 쌓이고, 세션을 끝내도 남아요.

설정 (/config 또는 /plugin configure memo-pad@k-mods)

  • palette: "메모 N" 배지의 색. 기본값 dark는 어두운 터미널용 노란 파스텔 칩이에요. 밝은 터미널이면 theme, 회색 배경 터미널이면 gray(원본처럼 색 없음)로 바꾸세요.

어떻게 동작하나요

  • 세션이 시작할 때: /m 명령을 등록하고, 지금 작업 폴더에 저장된 메모를 불러와요.
  • /m을 실행할 때: 메모 패널을 열거나(이미 열려 있으면) 닫아요.
  • 패널을 그릴 때: 입력창 하나와 메모 목록(메모마다 [넣기]/[x] 버튼)을 그려요. 메모를 추가·삭제할 때마다 이 플러그인 전용 저장소($.store)에 저장해요.
  • 힌트 줄을 그릴 때: 메모가 있고 패널이 닫혀 있으면, Claude Code가 원래 그리는 줄 끝에 메모 개수만 덧붙여요.

권한 (이 mod가 내 컴퓨터에서 하는 일)

이 플러그인 전용 저장소($.store)에 메모 텍스트를 저장해요. 그 외 네트워크·일반 파일·모델 호출은 전혀 없어요.

한계

  • 메모는 "작업 폴더" 단위로 나뉘어요. 같은 폴더 안에서는 브랜치를 바꾸거나 세션을 새로 시작해도 메모가 이어지지만, 폴더가 다르면 메모도 달라요.
  • [넣기]는 입력창에 글자를 채울 뿐, 자동으로 보내지는 않아요. Enter는 직접 눌러야 해요.

출처·라이선스

메인테이너(SeongGwangJu)가 자기 기기에서 쓰던 오리지널 모드를 공개용으로 옮긴 것으로, 외부 upstream은 없습니다. 저장소 루트의 MIT 라이선스를 따릅니다.

테스트한 Claude Code 버전: 2.1.291

Source 3 files
hooks/register.ts 114 lines
1// memo-pad: Claude가 일하는 동안 다음 지시를 적어두는 작은 메모장.
2//   /m 으로 열고 닫는다(작업 중에도 바로 열림). 6줄짜리 인라인 패널, Esc로 닫힘.
3//   메모마다 [넣기]는 프롬프트에 채우고(전송은 직접 Enter), [x]는 지운다.
4//   메모가 있으면 프롬프트 아래 힌트 줄 끝에 "메모 N"을 붙여서 잊지 않게 한다.
5//   폴더(작업 디렉터리)별로 따로 저장되고 세션이 끝나도 남는다.
6import type { On, PluginOptions } from 'claude-code'
7
8import { hintTail, storeKeyFor } from './notes'
9import { paletteOf } from './palette'
10
11const PANE = 'memo-pad'
12const ROWS = 6
13
14export function register(on: On, options: PluginOptions): void {
15  const palette = paletteOf(options.palette)
16
17  let notes: string[] = []
18  let storeKey: string | null = null
19  let isOpen = false
20
21  on('session.start', async ($, e, next) => {
22    await $.command.register({ name: 'm', description: '메모장 열기/닫기 (작업 중에도 열림)', immediate: true })
23    storeKey = storeKeyFor(await $.session.cwd())
24    const saved = await $.store.get(storeKey)
25    notes = Array.isArray(saved) ? (saved as string[]) : []
26    $.ui.invalidate('ui.render')
27    return next(e)
28  })
29
30  on('command.run', { command: 'm' }, async ($) => {
31    if (isOpen) {
32      await $.ui.close({ id: PANE })
33      isOpen = false
34      return {}
35    }
36    await $.ui.open({ id: PANE, title: '메모', focus: true, closeOnEscape: true, rows: ROWS })
37    isOpen = true
38    return {}
39  })
40
41  on('ui.close', async ($, e, next) => {
42    if (e.id === PANE) {
43      isOpen = false
44      $.ui.invalidate('ui.render')
45    }
46    return next(e)
47  })
48
49  // 힌트 줄 끝에 메모 개수. 엔진의 줄은 그대로 두고 tail만 붙인다.
50  on('ui.render', { component: 'PromptHint' }, async ($, e, next) => {
51    if (notes.length === 0 || isOpen) return next(e)
52    return next({ ...e, props: { ...e.props, tail: hintTail(notes.length) } })
53  })
54
55  on('ui.render', { component: 'Pane' }, async ($, e, next) => {
56    if (e.requestId !== PANE) return next(e)
57    // Input은 terminal·desktop에만 있다(Pane 자체는 모든 surface에서 열릴 수 있다). 그 밖의
58    // surface(vscode·mobile)에서는 엔진 기본 그림에 맡긴다.
59    if (e.surface !== 'terminal' && e.surface !== 'desktop') return next(e)
60    const { Box, Text, Button, Input } = $.ui.resolve(e)
61    const save = async (list: string[]) => {
62      notes = list
63      $.ui.invalidate('ui.render')
64      if (storeKey) await $.store.set(storeKey, notes)
65    }
66    // Text에는 key가 없다(목록 재조정용이 아니라 Box·Button의 key는 hover 범위·주소 용도).
67    const badge =
68      notes.length === 0
69        ? null
70        : palette.badgeBg
71          ? Text({ backgroundColor: palette.badgeBg, color: palette.badgeFg, children: ` 메모 ${notes.length} ` })
72          : Text({ children: `메모 ${notes.length}개` })
73    return Box({
74      flexDirection: 'column',
75      children: [
76        ...(badge ? [badge] : []),
77        Input({
78          key: 'new',
79          label: '메모',
80          placeholder: '다음에 시킬 일을 적고 Enter',
81          value: '',
82          submitLabel: '추가',
83          autoFocus: true,
84          onSubmit: async (value: string) => {
85            const text = value.trim()
86            if (text) await save([...notes, text])
87          },
88        }),
89        ...notes.map((note, i) =>
90          Box({
91            key: `row-${i}`,
92            flexDirection: 'row',
93            columnGap: 1,
94            children: [
95              Button({
96                key: `use-${i}`,
97                label: '넣기',
98                onPress: async () => {
99                  await $.prompt.fill({ text: note, mode: 'replace' })
100                  await save(notes.filter((_, j) => j !== i))
101                  await $.ui.close({ id: PANE })
102                  isOpen = false
103                },
104              }),
105              Button({ key: `del-${i}`, label: 'x', onPress: () => save(notes.filter((_, j) => j !== i)) }),
106              Text({ wrap: 'truncate-end', children: note }),
107            ],
108          }),
109        ),
110      ],
111    })
112  })
113}
114
hooks/notes.ts 12 lines
1// memo-pad의 순수 함수. $를 받지 않아 테스트하기 쉽다.
2
3/** 작업 디렉터리별 저장 키. 폴더마다 메모가 따로 쌓인다. */
4export function storeKeyFor(cwd: string): string {
5  return `notes:${cwd}`
6}
7
8/** 프롬프트 힌트 줄 끝에 붙는 꼬리 문구. */
9export function hintTail(count: number): string {
10  return `✎ 메모 ${count} (/m)`
11}
12
hooks/palette.ts 19 lines
1// memo-pad의 색 팔레트.
2//
3// 원본(메인테이너 전용 모드)은 색을 전혀 쓰지 않는 무채색 패널이었다. 공개판은 다른 두 k-mods
4// 모드와 같은 모양으로 palette 옵션을 두되, 솔직하게: 패널 위쪽에 "메모 N" 배지 칩 하나만
5// 테마 색으로 강조한다(CONTRIBUTING.md 화면 원칙 #2의 칩 패턴). "gray"는 원본처럼 색이 아예 없다.
6export type Palette = {
7  badgeBg: string | undefined
8  badgeFg: string | undefined
9}
10
11// 어두운 터미널 기준 파스텔 칩 (기본)
12const DARK: Palette = { badgeBg: '#E3C56A', badgeFg: '#1E2127' }
13const THEME: Palette = { badgeBg: 'remember', badgeFg: 'inverseText' }
14const GRAY: Palette = { badgeBg: undefined, badgeFg: undefined }
15
16export function paletteOf(name: unknown): Palette {
17  return name === 'gray' ? GRAY : name === 'theme' ? THEME : DARK
18}
19