# quick-replies

[← README로 돌아가기](../README.md)

> 반복해서 입력하던 "지금 진행상황"을 `$4`처럼 짧은 번호로 불러옵니다.

<p><picture>
<source media="(prefers-color-scheme: dark)" srcset="./images/showcase/preset-dark.gif">
<img alt="입력창에 $1을 치고 스페이스를 누르면 $1이 1번 프리셋 문구로 바뀐다" src="./images/showcase/preset-light.gif" width="560">
</picture></p>

| 항목 | 값 |
| --- | --- |
| 버전 | 0.13.2 |
| 자리 | 입력창 위 |
| 요구 사항 | Claude Code 2.1.296 이상(2.1.296~2.1.299에서 테스트). 입력창 위 영역을 표시할 수 있는 터미널과 데스크톱에서만 보입니다 |
| 외부 호출 | 긴 프리셋의 제목을 지을 때 haiku 호출. 세션 시작 때 `git rev-parse --show-toplevel` 한 번 |
| 저장 데이터 | 있음. 프리셋 목록과 접힘 상태를 모드 저장 공간(`$.store`)에 둡니다 |

## 목차

- [개요](#개요)
- [빠른 시작](#빠른-시작)
- [사용법](#사용법)
- [키와 명령](#키와-명령)
- [화면 구성](#화면-구성)
- [동작 방식](#동작-방식)
- [설정과 저장 데이터](#설정과-저장-데이터)
- [외부 호출과 사용량](#외부-호출과-사용량)
- [제한 사항](#제한-사항)
- [문제 해결](#문제-해결)
- [소스 구성](#소스-구성)
- [변경 이력](#변경-이력)

## 개요

자주 쓰는 문구를 프리셋으로 저장하면 입력창 위에 번호와 함께 표시됩니다. 입력창에 `$번호`를 치고 스페이스를 누르면 그 자리가 해당 문구로 바뀝니다. 번호를 클릭하면 입력창의 내용을 프리셋 문구로 바꾸고, 문구를 클릭하면 바로 보냅니다.

프리셋은 두 범위로 나뉩니다.

- **전역 프리셋**은 모든 저장소에서 보입니다.
- **로컬 프리셋**은 지금 작업하는 프로젝트에서만 보입니다.

`/ux-writing README 다듬어줘`처럼 `/`로 시작하는 프리셋은 스킬이나 슬래시 명령으로 실행합니다. 프리셋 추가, 문구 수정, 제목 지정, 삭제, 범위 이동, 순서 변경은 입력창에서 여는 편집 화면에서 할 수 있습니다.

이 모드가 하지 않는 것

- 프리셋을 파일로 내보내거나 가져오지 않습니다.
- 작업 중인 저장소에 파일을 만들지 않습니다.
- 다음에 보낼 요청은 [next-prompts](next-prompts.md)가 제안합니다. 이 모드는 해당 제안 뒤부터 프리셋 번호를 매깁니다.

## 빠른 시작

1. 마켓플레이스를 등록했다면 다음 명령으로 설치하고, Claude Code를 다시 시작하거나 `/reload-plugins`를 실행합니다.

   ```
   /plugin install quick-replies@oobg
   ```

2. 입력창 위에 기본 프리셋 두 개가 보입니다.

   ```
    전역  $e: 편집  $0: 접기
     $1: 지금 진행상황
     $2: 맞아 그렇게 진행해
   ```

3. 입력창에 `$1`을 치고 스페이스를 누르면 그 자리가 "지금 진행상황"으로 바뀝니다. Enter를 눌러 보냅니다.
4. 프리셋을 추가하려면 입력창에 `$e`를 치고, `o`를 누른 뒤 문구를 작성하고 Enter를 누릅니다. `q`로 편집 화면을 닫습니다.

## 사용법

### 프리셋 범위

- 처음 설치하면 전역 프리셋에 "지금 진행상황", "맞아 그렇게 진행해" 두 개가 들어 있습니다. 로컬 프리셋은 비어 있습니다.
- 기본 프리셋 두 개는 전역 목록을 한 번도 저장하지 않았을 때만 사용됩니다. 프리셋을 추가, 수정, 삭제하거나 다른 범위로 옮기면 변경된 전역 목록이 저장됩니다. 따라서 기본 프리셋을 삭제해도 다시 생기지 않으며, 전역 프리셋을 모두 삭제하면 빈 목록으로 유지됩니다.
- `git` 저장소 안에서 작업하면 저장소의 최상위 폴더를 로컬 프리셋의 기준으로 사용합니다. 저장소 밖이거나 git을 실행할 수 없으면 세션 폴더(Claude Code를 연 폴더)를 기준으로 삼습니다. 기준 폴더는 세션 시작 시 한 번 정하며, 세션 도중에는 바뀌지 않습니다.
- 다른 세션에서 프리셋을 고치면 이미 열려 있는 세션에도 5초 안에 반영됩니다. 같은 기준 폴더의 로컬 프리셋도 마찬가지입니다. 이 기능이 들어간 버전을 설치하기 전에 연 세션은 다시 시작해야 반영됩니다.

### 번호로 불러오기

- 입력창에 `$5`처럼 입력하고 스페이스를 누르면 `$`와 숫자가 해당 번호의 프리셋 문구로 바뀌고, 이어서 쓸 수 있게 문구 뒤에 스페이스가 하나 붙습니다. 스페이스로 번호의 끝을 알리므로 `$12`처럼 두 자리 번호도 쓸 수 있습니다. 문장 중간에서도 사용할 수 있습니다. 예를 들어 `그리고 $3`을 치고 스페이스를 누르면 `그리고 지금 진행상황 `이 됩니다.
- `$`는 입력창의 맨 앞이나 공백 바로 뒤에 있어야 합니다. `a$3`처럼 앞에 다른 글자가 붙어 있으면 바꾸지 않습니다.
- 해당 번호의 프리셋이 없으면 입력한 글자가 그대로 남습니다.
- 스페이스 없이 `$12`에서 바로 Enter를 눌러도 보낼 때 해당 프리셋 문구로 바꿔 보냅니다. 입력창에는 바뀐 글이 보이지 않고, 보내는 내용만 바뀝니다. 글 전체가 `/명령` 프리셋 하나의 번호(`$4`)이면 문구를 클릭했을 때처럼 명령으로 실행합니다.
- 번호를 클릭하면 입력창의 내용을 그 문구로 바꿉니다. 보내지는 않습니다.
- 문구를 클릭하면 바로 보냅니다. 대화 기록에는 사용자가 보낸 메시지로 남습니다.
- 펼친 상태에서는 프리셋 하나를 한 줄로 표시합니다. 문구의 줄바꿈은 공백으로 이어 붙이고, 터미널 폭을 넘는 부분은 말줄임표(`…`)로 자릅니다. 전체 문구는 편집 화면에서 볼 수 있으며, 클릭하거나 `$번호`로 넣을 때는 전체 문구를 사용합니다.

### 번호 매기기

- 전역 프리셋에 먼저 번호를 매기고, 이어서 로컬 프리셋에 매깁니다.
- [next-prompts](next-prompts.md)의 다음 제안이 떠 있으면 프리셋 번호는 제안 개수 다음부터 시작합니다. 다음 제안이 3개면 첫 프리셋은 `$4`, 2개면 `$3`입니다.
- 다음 제안이 없거나 Claude가 작업 중이면 `$1`부터입니다. next-prompts를 설치하지 않았을 때도 `$1`부터입니다.
- 모든 프리셋에 번호가 붙습니다. 프리셋이 많으면 `$10:`, `$12:`처럼 두 자리 번호가 됩니다.

### 스킬과 슬래시 명령 프리셋

- `/이름` 또는 `/이름 나머지 글` 형태의 프리셋을 클릭하면 `이름`을 명령 이름으로, 나머지 글을 인자로 사용해 스킬이나 명령을 실행합니다. 예를 들어 `/ux-writing README 다듬어줘`는 `ux-writing` 명령을 인자 `README 다듬어줘`로 실행하고, `/compact`는 인자 없이 실행합니다.
- 실행 전에 지금 쓸 수 있는 명령 목록에서 이름을 찾습니다. 없으면 실행하지 않고 "/이름에 해당하는 명령이나 스킬을 찾지 못했어요"라는 알림만 띄웁니다.
- 명령 이름에 `/`가 들어 있으면(`/usr/bin`처럼) 명령으로 보지 않고 일반 문구로 보냅니다.
- 번호를 클릭하거나 `$번호`로 넣을 때는 명령으로 실행하지 않고 문구만 입력창에 넣습니다.

### 접기와 자동 제목

`$0: 접기`를 클릭하거나 입력창에 `$0`을 치면 전역과 로컬 프리셋이 각각 한 줄로 접히고 제목만 표시됩니다. 다시 실행하면 펼쳐집니다. 접힘 상태는 저장되므로 다음 세션에도 유지됩니다. 다음 제안은 접히지 않고 항상 전체 문구가 표시됩니다.

<p><picture>
<source media="(prefers-color-scheme: dark)" srcset="./images/showcase/fold-dark.gif">
<img alt="$0을 치면 프리셋을 한 줄로 접고, 다시 치면 펼친다" src="./images/showcase/fold-light.gif" width="560">
</picture></p>

아래는 다음 제안이 3개 표시된 상태에서 프리셋을 접은 모습입니다. 프리셋 번호는 `$4`부터 시작합니다.

```
 전역  $e: 편집  $0: 펼치기
   $4: 진행상황 | $5: 맞아 그렇게 진행해
 로컬
   $6: /ux-writing…
```

- 접힌 줄에는 프리셋의 제목을 보여 줍니다. 제목이 없으면 문구를 12자로 줄이고 끝에 `…`를 붙입니다. 12자 이하인 문구는 그대로 보입니다.
- 문구가 12자보다 길고 제목이 없는 프리셋에는 작은 모델(haiku)이 12자 이내의 제목을 만들어 줍니다. 제목은 세션 시작 시, 프리셋 추가나 문구 수정 직후, 제목을 비워 저장한 직후에 생성합니다. 생성한 제목은 저장하므로 다시 만들지 않습니다.
- 제목 생성이 아직 끝나지 않았거나 모델 호출에 실패하면 줄인 문구가 표시됩니다. 위 예시의 `/ux-writing…`이 그 경우입니다.
- 문구를 수정하면 모델이 자동 생성한 제목은 삭제하고 새로 만듭니다. 직접 지정한 제목은 그대로 유지합니다.
- 프리셋을 접어도 클릭하거나 `$번호`로 입력할 때는 원래 문구 전체를 사용합니다.

### 편집 화면

<p><picture>
<source media="(prefers-color-scheme: dark)" srcset="./images/showcase/editor-dark.gif">
<img alt="$e로 편집 화면을 열고, 입력창에 쓴 문구를 Enter로 저장한다" src="./images/showcase/editor-light.gif" width="560">
</picture></p>

입력창에 `$e`를 치거나 `$e: 편집`을 클릭하면 입력창 위 영역에 편집 화면이 열립니다. 편집 중에도 포커스는 입력창에 유지됩니다. 이때 입력창에서 누른 키는 모드가 편집 명령으로 처리하며, 다음 제안은 표시되지 않습니다.

```
 프리셋 편집  o: 전역에 추가  q: 완료
 전역 (모든 저장소)
 ▸ 1: 지금 진행상황
   2: 맞아 그렇게 진행해
 로컬 (claude-code-mods)
   3: /ux-writing 문구 다듬어줘
 고른 프리셋  i: 문구 수정  t: 제목  dd: 삭제  m: 로컬로  K: 위로  J: 아래로
```

편집 화면에는 두 가지 상태가 있습니다.

**고르는 중.** 편집 화면을 처음 열면 이 상태로 시작합니다. 입력창에 친 글자는 입력되지 않고 [키와 명령](#키와-명령)의 편집 명령으로 처리됩니다. 붙여넣기와 지우기도 입력창에 반영되지 않습니다. 키는 vim과 비슷하게 동작합니다. `j`, `k`로 커서를 아래나 위로 옮기고, `gg`, `G`로 처음이나 끝으로 갑니다. 숫자 `1`~`9`로 전역과 로컬을 통틀어 위에서부터 해당 번호의 프리셋을 선택합니다. 10번째부터는 `j`, `k`로 옮기거나 프리셋 줄을 클릭해 선택합니다. 선택한 줄 앞에는 `▸`가 붙고, 맨 아래에는 해당 프리셋에서 사용할 수 있는 키가 표시됩니다. 입력창 아래 안내 줄에는 `-- NORMAL --`이 표시됩니다. 이 상태에서 Enter를 누르면 메시지 대신 "프리셋 편집 중이에요. q로 편집을 끝낸 뒤 보내 주세요"라는 안내가 표시됩니다.

**쓰는 중.** `o`, `i`, `t`를 누르면 입력창에서 새 문구를 작성하거나 기존 문구와 제목을 수정할 수 있습니다. 수정할 때는 기존 값이 미리 채워집니다. 모드에서 입력창 테두리 색은 바꿀 수 없으므로, 글자 색으로 편집 중임을 표시합니다. 입력창 아래 안내 줄에는 `-- INSERT --  Enter 저장 · $q 취소`가 표시됩니다.

- Enter를 누르면 앞뒤 공백을 제거하고 저장한 뒤 입력창을 비웁니다. 저장 결과는 보내기 차단 안내 자리에 "프리셋을 추가했어요", "문구를 고쳤어요", "제목을 바꿨어요" 중 하나로 표시됩니다.
- 문구가 비어 있으면 저장하지 않고 "문구가 비어 있어서 저장하지 않았어요"라고 알려 줍니다.
- 제목을 빈 값으로 저장하면 기존 제목이 삭제됩니다. 문구가 12자보다 긴 프리셋은 모델이 제목을 다시 생성합니다.
- 직접 지정한 제목은 12자를 넘어도 자르지 않습니다. 접힌 줄에도 전체 제목이 그대로 표시됩니다.
- 같은 문구가 이미 있어도 새 프리셋으로 저장할 수 있습니다.
- 입력을 취소하려면 `$q`를 치거나 `$q: 취소`를 클릭합니다. 아무것도 저장하지 않고 입력창을 비우며, 기존 문구와 제목은 그대로 남습니다.
- 저장하거나 취소하면 고르는 중으로 돌아갑니다.

`dd`로 지운 프리셋은 `u`로 되살릴 수 있습니다. 마지막으로 지운 프리셋 하나가 원래 범위의 원래 자리로 돌아오고, 커서도 그 프리셋에 놓입니다. 편집 화면을 닫으면 되살릴 수 없습니다.

편집 화면을 닫으려면 고르는 중에 `q`를 누르거나 `완료`를 클릭합니다. 쓰는 중에는 `q`가 일반 글자로 입력되므로 먼저 `$q`로 입력을 취소해야 합니다.

## 키와 명령

입력창에서 쓰는 것(편집 화면이 닫혀 있을 때)

| 입력 | 동작 | 조건 |
| --- | --- | --- |
| `$1 `, `$12 `처럼 번호 뒤 스페이스 | `$`, 숫자, 친 스페이스를 그 번호 프리셋의 문구와 스페이스 하나로 바꿉니다 | `$`가 맨 앞이나 공백 뒤. 한 글자씩 친 경우만(붙여넣기 제외). 그 번호의 프리셋이 있을 때 |
| `$e` | 편집 화면을 엽니다. 친 `$e`는 남기지 않습니다 | `$` 위치 조건은 위와 같습니다. 소문자만 |
| `$0` | 프리셋을 접거나 폅니다. 친 `$0`은 남기지 않습니다 | `$` 위치 조건은 위와 같습니다 |

입력창 위 영역 클릭

| 입력 | 동작 | 조건 |
| --- | --- | --- |
| 번호(`$4:`) 클릭 | 입력창 내용을 그 문구로 바꿉니다. 보내지 않습니다 | |
| 문구 클릭 | 바로 보냅니다. `/`로 시작하면 스킬이나 명령으로 실행합니다 | 없는 명령이면 알림만 |
| `$e: 편집` 클릭 | 편집 화면을 엽니다 | |
| `$0: 접기`, `$0: 펼치기` 클릭 | 접거나 폅니다 | |

편집 화면, 고르는 중

| 입력 | 동작 | 조건 |
| --- | --- | --- |
| `j`, `k` | 커서를 한 칸 아래나 위로 옮깁니다 | 고른 프리셋이 없으면 `j`는 첫 프리셋, `k`는 마지막 프리셋을 고릅니다 |
| `gg`, `G` | 첫 프리셋이나 마지막 프리셋을 고릅니다 | |
| `1`~`9` | 그 번호의 프리셋을 고릅니다 | 전역과 로컬을 통틀어 9번째까지 |
| 프리셋 줄 클릭 | 그 프리셋을 고릅니다 | 10번째 이후도 가능 |
| `o` | 커서가 있는 범위에 추가할 문구를 쓰기 시작합니다 | 고른 프리셋이 없거나 전역에 있으면 전역, 로컬에 있으면 로컬 |
| `i` | 고른 프리셋의 문구를 고쳐 씁니다 | 고른 프리셋이 있을 때 |
| `t` | 고른 프리셋의 제목을 씁니다 | 고른 프리셋이 있을 때 |
| `dd` | 고른 프리셋을 삭제하고, 커서를 그 자리에 들어선 프리셋(맨 뒤였으면 앞의 프리셋)으로 옮깁니다 | 고른 프리셋이 있을 때. 첫 `d` 뒤에 다른 키를 치면 취소됩니다 |
| `u` | 마지막으로 지운 프리셋을 원래 범위의 원래 자리에 되살리고 커서를 둡니다 | 이 편집 화면에서 지운 프리셋이 있을 때 |
| `m` | 고른 프리셋을 전역과 로컬 사이로 옮깁니다. 옮긴 쪽 목록의 맨 뒤에 붙습니다 | 고른 프리셋이 있을 때 |
| `K`, `J` | 선택한 프리셋을 같은 목록 안에서 한 칸 위나 아래로 옮깁니다 | 고른 프리셋이 있을 때. 목록 끝에서는 그대로 |
| `q` | 편집 화면을 닫습니다 | |
| Enter | 보내지 않고 안내를 보여 줍니다 | |

편집 키는 대소문자를 구분합니다. `j`와 `J`, `k`와 `K`는 서로 다른 동작입니다. 해당 키가 표시된 버튼을 클릭해도 동일하게 동작하며, `dd: 삭제`는 한 번 클릭하면 삭제를 준비만 하고 한 번 더 클릭해야 지웁니다. `ctrl+x tab` 또는 클릭으로 입력창 위 영역에 포커스를 옮긴 뒤에는 해당 영역의 버튼 단축키로도 실행할 수 있습니다. 단축키는 소문자 한 글자 버튼(`o`, `u`, `q`, `i`, `t`, `m`)에만 있고, `dd`, `K`, `J` 버튼에는 없습니다.

편집 화면, 쓰는 중

| 입력 | 동작 | 조건 |
| --- | --- | --- |
| 글자 | 입력창에 입력되고 강조 색으로 표시됩니다 | |
| Enter | 저장하고 고르는 중으로 돌아갑니다 | 빈 문구는 저장하지 않습니다. 빈 제목은 제목을 지웁니다 |
| `$q`, `$q: 취소` 클릭 | 저장하지 않고 입력창을 비웁니다 | `$`가 맨 앞이나 공백 뒤 |

## 화면 구성

보기 화면(편집 화면이 닫혀 있을 때)

| 요소 | 내용 | 표시 조건 |
| --- | --- | --- |
| 상단 줄 | 흐린 `전역` 글자, `$e: 편집`, `$0: 접기` 또는 `$0: 펼치기`. `$e:`, `$0:`은 강조 색, 뒤의 말은 흐린 색 | 늘 |
| 전역 프리셋 | 펼치면 한 줄에 하나씩 표시합니다. 문구의 줄바꿈은 공백으로 잇고, 폭을 넘으면 말줄임표로 자릅니다. 접으면 한 줄에 `$4: 제목 \| $5: 제목`처럼 이어 붙입니다. 줄이 넘치면 다음 줄로 넘어갑니다 | 전역 프리셋이 하나 이상 |
| `로컬` 제목 | 흐린 글자 | 로컬 프리셋이 하나 이상 |
| 로컬 프리셋 | 전역 프리셋과 같은 모양 | 로컬 프리셋이 하나 이상 |
| 번호 `$n:` | 강조 색 | 프리셋마다 |
| 명령 부분 | 표시된 문구가 `/이름`으로 시작하면 해당 부분만 번호와 같은 강조 색으로 표시 | 접힌 줄에서는 제목이 `/이름`으로 시작할 때만 |
| 구분선 `\|` | 흐린 글자 | 접힌 줄에서 프리셋 사이 |

편집 화면

| 요소 | 내용 | 표시 조건 |
| --- | --- | --- |
| 상단 줄 | 굵은 `프리셋 편집`, `o: 전역에 추가`(커서가 로컬에 있으면 `o: 로컬에 추가`), `u: 되살리기`, `q: 완료` | 늘. `u: 되살리기`는 지운 프리셋이 있을 때만 |
| 전역 목록 | `전역 (모든 저장소)` 아래에 프리셋 문구가 표시됩니다. 9번째까지는 앞에 강조 색 번호(`1:`)가 붙고, 제목이 있으면 뒤에 `제목: …`가 흐리게 표시됩니다 | 늘. 비어 있으면 `(비어 있음)` |
| 로컬 목록 | `로컬 (<기준 폴더 이름>)` 아래 같은 모양 | 늘. 비어 있으면 `(비어 있음)` |
| 고른 표시 `▸` | 고른 프리셋 줄 앞, 강조 색 | 고른 프리셋이 있을 때 |
| 입력 줄 | 강조 색 `제목`(`전역에 추가`, `로컬에 추가`, `문구 수정`, 제목), 흐린 안내 문구, `$q: 취소` | 쓰는 중 |
| 아래 줄 | 선택 전에는 "입력창에 j, k 또는 프리셋 번호를 쳐서 고르면 문구 수정, 제목 달기, 삭제, 이동을 할 수 있어요."가 표시됩니다. 선택 후에는 `고른 프리셋  i: 문구 수정  t: 제목  dd: 삭제  m: 로컬로(또는 전역으로)  K: 위로  J: 아래로`가 표시됩니다 | 늘 |
| 입력창 아래 안내 줄 | 고르는 중에는 `-- NORMAL --`, 쓰는 중에는 `-- INSERT --  Enter 저장 · $q 취소`. `dd`, `gg`의 첫 글자를 친 뒤에는 `-- NORMAL --  d`처럼 그 글자가 옆에 붙습니다 | 편집 화면이 열려 있을 때 |

색과 위치

- 강조 색에는 Claude Code 테마의 `suggestion` 색을 사용합니다. 입력창의 `/명령`, 버튼 번호, 편집 중인 입력창 글자에도 같은 색을 적용합니다.
- 프리셋은 입력창 위 영역에서 next-prompts의 다음 제안 아래에 표시됩니다. 각 줄 위에는 한 칸씩 여백을 둡니다.
- 설문이 입력창 위 영역을 사용 중이면 이 모드는 아무것도 표시하지 않습니다.

## 동작 방식

| 이벤트 | 하는 일 | 소스 |
| --- | --- | --- |
| `session.start` | 기준 폴더를 정한 뒤 전역, 로컬 프리셋과 접힘 상태를 저장소에서 읽어 `$.state`에 저장합니다. 이어서 제목이 없는 긴 프리셋의 제목을 순서대로 생성합니다(완료를 기다리지 않음). 5초(`SYNC_MS`)마다 전역과 현재 기준 폴더의 로컬 프리셋을 저장소에서 다시 읽어, 값이 달라졌을 때만 `$.state`를 바꾸도록 `$.clock.every`를 등록합니다 | `hooks/register.tsx`: `load`, `titleAll`, `sync` |
| `prompt.edit` | 보기 화면에서는 `$1 `, `$12 `처럼 번호 뒤에 친 스페이스를 보고 프리셋 문구로 바꾸고, `$e`로 편집 화면을 열며, `$0`으로 프리셋을 접거나 펼칩니다. 편집 화면에서 고르는 중에는 입력을 차단하고 한 글자 키를 편집 명령으로 처리합니다. 쓰는 중에는 입력을 그대로 전달하고 글자 색을 바꾸며 `$q`를 취소 명령으로 처리합니다 | `hooks/register.tsx`: `register`, `editorKey`, `cancelInput` |
| `prompt.submit` | 보기 화면에서는 입력창에서 보낸 글에 남은 `$번호`(스페이스 없이 Enter)를 프리셋 문구로 바꿔 보냅니다. 글 전체가 `/명령` 프리셋 하나면 보내지 않고 명령으로 실행합니다. 편집 화면이 열려 있을 때 입력창에서 전송을 시도하면 차단합니다. 쓰는 중이라면 입력한 글을 저장하고, 차단 사유 대신 저장 결과 메시지를 반환합니다 | `hooks/register.tsx`: `register`, `submitInput`, `send`, `hooks/band.ts`: `expandNumbers` |
| `ui.render` (`PromptHint`) | 편집 화면이 열려 있으면 입력창 아래 흐린 안내 줄(`hint`)을 고르는 중에는 `-- NORMAL --`(두 번 치는 키의 첫 글자가 있으면 그 글자도), 쓰는 중에는 `-- INSERT --  Enter 저장 · $q 취소`로 바꿉니다 | `hooks/register.tsx`: `register` |
| `ui.render` (`AbovePrompt`) | 보기 화면에서는 입력창 위 공용 영역의 20번 순서에 프리셋을 추가합니다. 편집 화면에서는 해당 영역을 단독으로 사용하는 틀을 반환합니다 | `hooks/register.tsx`: `register`, `hooks/band.ts`: `withBandSection`, `exclusiveBand` |

`$` 입력 판정(`hooks/band.ts`: `typedAfterDollar`, `typedNumber`)

`$e`, `$0`, `$q`는 다음 조건을 모두 만족할 때만 `$` 뒤의 글자 하나를 명령으로 인식합니다(`typedAfterDollar`).

- 한 번의 키 입력으로 들어온 글자여야 합니다. 붙여넣기나 여러 키가 한꺼번에 처리된 입력은 제외합니다.
- 친 글자가 `0`~`9` 또는 소문자 `a`~`z` 한 글자입니다.
- 선택 영역을 덮어쓰지 않고 커서 위치에 글자를 입력해야 합니다.
- 커서 바로 앞 글자가 `$`이고, 그 `$`가 입력창 맨 앞이거나 공백 바로 뒤에 있습니다.

`typedNumber`는 번호를 판정합니다. 한 번의 키 입력으로 스페이스를 쳤고 선택 영역이 없으며, 커서 앞이 `$` 뒤에 `1`~`9`로 시작하는 숫자가 이어진 모양이고 그 `$`가 입력창 맨 앞이거나 공백 바로 뒤일 때 번호와 `$`의 위치를 반환합니다. 문구로 치환할 때는 `$`부터 친 스페이스까지 함께 바꾸고, 문구 뒤에 스페이스를 하나 붙입니다.

번호 계산

- 입력창 위 영역에 표시할 번호는 `isWorking`일 때 0부터, 그 외에는 next-prompts의 `suggestions`에서 문자열 항목을 센 개수부터 시작해 1씩 증가시킵니다.
- 입력창의 `$n `을 `n - 1 - (제안 개수)`번째 프리셋 문구로 바꿉니다(전역 다음 로컬 순). 계산한 위치가 범위를 벗어나면 입력을 그대로 둡니다. `$1`~`$제안 개수`는 next-prompts가 처리하므로 이 모드에서는 변경하지 않습니다.
- next-prompts의 상태는 이 모드에서 관리하지 않으므로 데이터 형식을 확인하고 유효한 항목만 읽습니다. 읽을 수 없으면 제안이 0개인 것으로 처리합니다.

띠 나눠 쓰기

- 입력창 위 영역에는 UI 트리 하나만 렌더링할 수 있어 next-prompts와 `oobg-band` 키를 사용하는 공용 틀을 공유합니다. 각 모드는 `oobg-band:<순서>:<이름>` 키가 붙은 Box를 추가하며, Box는 순서대로 정렬됩니다. 이 모드의 키는 `oobg-band:20:quick-replies`이고 next-prompts의 순서는 10입니다.
- 편집 화면에서는 `oobg-band-exclusive` 키가 붙은 틀을 반환합니다. 다른 모드는 이 키를 확인하면 자체 줄을 추가하지 않으므로 편집 중에는 다음 제안이 숨겨집니다.
- `hooks/band.ts`는 next-prompts와 같은 파일을 씁니다.

상태(`$.state`)와 수명

| 키 | 값 | 수명 |
| --- | --- | --- |
| `repo` | 로컬 프리셋의 기준 폴더 경로. 읽기 전에는 `null` | 세션 동안. 모드 코드를 다시 불러와도 남습니다 |
| `globalReplies` | 전역 프리셋 목록. 읽기 전에는 `null` | 같음 |
| `projectReplies` | 로컬 프리셋 목록. 읽기 전에는 `null` | 같음 |
| `mode` | `{ kind: 'view' }` 또는 `{ kind: 'edit', selected, input, pending?, deleted? }`. `selected`는 커서가 있는 프리셋 id, `input`은 쓰는 중인 입력(`add`, `rename`, `title`), `pending`은 `dd`, `gg`의 첫 글자, `deleted`는 `u`로 되살릴 마지막으로 지운 프리셋과 그 범위, 위치 | 같음. 처음은 `view`. `pending`과 `deleted`는 편집 화면을 닫으면 사라집니다 |
| `compact` | 접혀 있는가. 읽기 전에는 `null` | 같음 |

렌더링 훅에서는 상태를 수정할 수 없으므로 위 값이 `null`이면 `$.store`에서 직접 읽습니다.

제목 짓기

- 제목이 없는 프리셋 중 문구가 12자(`MAX_TITLE_CHARS`)보다 긴 경우에만 제목을 생성합니다. 글자 수는 문자열 길이로 계산합니다.
- 같은 프리셋과 문구에 대한 제목 생성 요청이 진행 중이면 다시 호출하지 않습니다(메모리의 `titling` 표).
- 모델 응답의 첫 줄에서 `제목:`, 번호, 글머리 기호, 양끝을 감싼 따옴표를 제거하고 12자를 넘는 부분은 잘라 냅니다. 제거하고 남은 글자가 없으면 저장하지 않습니다.
- 모델 응답이 도착하기 전에 문구가 바뀌었거나 사용자가 직접 제목을 지정했다면 해당 응답을 버립니다. 자동 생성한 제목에는 `autoTitle: true` 표시가 붙습니다.

## 설정과 저장 데이터

별도 설정 항목은 없습니다. 프리셋은 편집 화면에서 관리하고, 접기와 펼치기는 `$0`으로 전환합니다.

`$.store` 키

| 키 | 값 | 기본값 |
| --- | --- | --- |
| `global` | 전역 프리셋 배열 | 없으면 기본 프리셋 두 개(id `default-0`, `default-1`) |
| `repo:<기준 폴더 절대 경로>` | 그 폴더의 로컬 프리셋 배열 | 없으면 빈 배열 |
| `compact` | 접힘 여부(boolean) | 없으면 `false`(펼침) |

프리셋 하나는 `{ id, text, title?, autoTitle? }` 구조입니다. `text`는 전송할 문구, `title`은 접힌 줄에 표시할 제목, `autoTitle`은 모델이 생성한 제목인지 나타내는 값입니다. 새 프리셋의 id는 `crypto.randomUUID()`로 생성합니다.

- 프리셋을 변경할 때마다 `global`과 현재 기준 폴더의 `repo:` 키를 함께 저장합니다. 전역과 로컬 사이에서 프리셋을 옮겨도 두 목록을 함께 저장하기 위해서입니다.
- 열려 있는 세션은 5초마다 `global`과 현재 기준 폴더의 `repo:` 키를 다시 읽고, 값이 달라졌을 때만 화면을 바꿉니다. 그래서 다른 세션에서 고친 프리셋이 이미 열린 세션에도 반영됩니다. 접힘 상태는 다시 읽지 않습니다. 이 기능이 들어간 버전을 설치하기 전에 연 세션은 다시 시작해야 합니다.
- `$.store`는 사용자의 Claude Code 설정 폴더 안에 있는 이 모드 전용 JSON 파일입니다. 정확한 파일 경로는 확인되지 않았습니다. 저장 공간 전체는 JSON 텍스트 4 MiB까지입니다.
- 작업 중인 저장소에는 파일을 만들지 않습니다.
- 별도의 초기화 명령은 없습니다. 프리셋은 편집 화면에서 하나씩 삭제해야 합니다.

## 외부 호출과 사용량

모델 호출

| 항목 | 값 |
| --- | --- |
| 모델 | `haiku` |
| 보내는 내용 | 시스템 지시(12자 이내 제목 하나만 쓸 것, 문구의 언어와 말투를 따를 것, `/` 명령은 하는 일로 지을 것)와 프리셋 문구 하나. 대화 기록은 보내지 않습니다 |
| 제한 | `maxTokens: 40`, `effort: 'low'`, `timeoutMs: 20_000` |
| 시점 | 세션 시작 때 대상 프리셋마다 한 번씩 차례로, 프리셋 추가와 문구 수정 직후, 제목을 비워 저장한 직후 |
| 실패 시 | 알림 없이 줄인 문구를 표시하고, 다음 세션 시작 시 다시 시도합니다 |
| 사용량 | 세션과 같은 API 클라이언트와 계정으로 호출하므로 사용자의 Claude Code 사용량에 포함됩니다 |

모델 호출만 비활성화하는 설정은 없습니다. 문구가 12자 이하이거나 직접 제목을 지정하면 모델을 호출하지 않습니다.

하위 프로세스

| 항목 | 값 |
| --- | --- |
| 명령 | `git rev-parse --show-toplevel` |
| 시점 | 세션 시작 때 기준 폴더를 정할 때. 상태가 채워지기 전에 그리기가 먼저 오면 그때도 실행할 수 있습니다 |
| 제한 시간 | 코드에서 따로 정하지 않음 |
| 실패 시 | 종료 코드가 0이 아니거나 출력이 비었거나 실행하지 못하면 세션 폴더를 씁니다 |

네트워크 호출은 모델 호출 외에 없습니다.

## 제한 사항

- 편집 화면의 숫자 키는 `1`~`9`까지입니다. 10번째 이후 프리셋은 `j`, `k`로 옮기거나 클릭해서 고릅니다.
- `$` 명령은 키를 하나씩 입력할 때만 동작합니다. `$3 `을 붙여넣으면 바뀌지 않으며, `$E`처럼 대문자로 입력해도 인식하지 않습니다.
- 입력창에서 번호가 바로 바뀌려면 스페이스를 쳐야 합니다. `$12`에서 바로 Enter를 누르면 입력창에는 보이지 않고 보내는 내용만 바뀝니다.
- 기준 폴더는 세션을 시작할 때 정합니다. 세션 도중 다른 저장소로 옮겨 가도 로컬 프리셋은 바뀌지 않습니다.
- 편집 화면이 열려 있는 동안에는 입력창으로 Claude에게 메시지를 보낼 수 없습니다.
- 입력창 위 영역을 지원하는 터미널과 데스크톱 화면에서만 표시됩니다.
- 프리셋 내보내기와 가져오기 기능은 없습니다.

## 문제 해결

| 증상 | 원인 | 해결 |
| --- | --- | --- |
| `$3`을 쳤는데 글자가 그대로 남습니다 | 번호 뒤에 스페이스를 치지 않았거나, `$` 앞에 다른 글자가 붙어 있거나, 붙여넣었거나, 해당 번호의 프리셋이 없습니다. 번호가 next-prompts 제안에 해당하면 next-prompts에서 처리합니다 | `$` 앞에 공백을 두고 한 글자씩 입력한 뒤 스페이스를 누릅니다. 입력창 위에 표시된 번호를 확인합니다 |
| 입력창에 글자가 들어가지 않습니다 | 편집 화면에서 프리셋을 고르는 중에는 키 입력을 편집 명령으로 처리합니다 | `q`로 편집 화면을 닫습니다 |
| Enter를 눌렀는데 "프리셋 편집 중이에요. `q`로 편집을 끝낸 뒤 보내 주세요"가 뜹니다 | 편집 화면이 열려 있습니다 | q로 닫고 다시 보냅니다 |
| 쓰는 중에 `q`를 눌렀는데 편집 화면이 닫히지 않습니다 | 쓰는 중에는 `q`가 글자로 들어갑니다 | `$q`로 입력을 취소한 뒤 `q`를 누릅니다 |
| `/이름` 프리셋을 클릭하면 "…찾지 못했어요" 알림만 뜹니다 | 현재 사용할 수 있는 명령이나 스킬 목록에 해당 이름이 없습니다 | 스킬이나 플러그인이 설치되어 있는지 확인하고 프리셋 문구의 이름을 고칩니다 |
| 다른 저장소에서 쓰던 로컬 프리셋이 안 보입니다 | 로컬 프리셋은 기준 폴더마다 따로 저장됩니다. git 밖에서는 세션 폴더가 기준입니다 | 그 저장소 안에서 세션을 엽니다. 모든 곳에서 쓰려면 `m`으로 전역에 옮깁니다 |
| 접힌 줄에 `…`로 잘린 문구가 보입니다 | 제목 생성이 아직 끝나지 않았거나 모델 호출이 실패했습니다 | `t`로 직접 제목을 답니다. 다음 세션 시작 때 다시 짓습니다 |
| 프리셋 줄이 보이지 않습니다 | 설문이 입력창 위 영역을 사용 중이거나, 현재 화면이 입력창 위 영역을 지원하는 터미널 또는 데스크톱 화면이 아닙니다 | 설문을 끝냅니다 |
| 지운 기본 프리셋이 돌아오지 않습니다 | 기본 프리셋은 전역 목록을 한 번도 저장하지 않았을 때만 쓰입니다 | 지운 편집 화면이 열려 있으면 `u`로 되살리고, 닫았다면 `o`로 다시 추가합니다 |
| 다른 세션에서 고친 프리셋이 이 세션에 보이지 않습니다 | 이 세션이 프리셋을 다시 읽는 기능이 들어간 버전보다 먼저 열렸습니다 | 세션을 다시 시작합니다. 그 뒤로는 5초 안에 반영됩니다 |

## 소스 구성

| 파일 | 역할 |
| --- | --- |
| `mods/quick-replies/.claude-plugin/plugin.json` | 플러그인 이름, 버전, 설명, 타입 경로 |
| `mods/quick-replies/.claude-plugin/types/` | 엔진 타입 선언. git에 올리지 않습니다(`.gitignore`) |
| `mods/quick-replies/hooks/hooks.json` | 훅 모듈 목록(`./register.tsx`) |
| `mods/quick-replies/hooks/register.tsx` | 훅 등록, 프리셋 읽기와 저장, `$` 입력 처리, 편집 화면, 띠 그리기, 명령 실행, 제목 짓기 호출 |
| `mods/quick-replies/hooks/title.ts` | 제목 길이 상수, 모델 지시문과 프롬프트, 모델 응답 정리, 축약 제목 |
| `mods/quick-replies/hooks/band.ts` | next-prompts와 입력창 위 영역을 공유하는 틀, `$` 입력 판정, 번호 표시 |
| `mods/quick-replies/types/index.d.ts` | `Reply`, `Scope`, `EditInput`, `Deleted`, `Mode` 타입과 `$.state` 키 선언 |
| `mods/quick-replies/tests/quick-replies.test.tsx` | 번호, 클릭, 편집 키, 접기, 제목, 범위, 입력창 위 영역 공유, 명령 실행, `$` 입력 테스트 |
| `mods/quick-replies/tsconfig.json` | 타입 검사 설정 |

## 변경 이력

- **0.13.2**: `$번호` 뒤에 스페이스를 쳐서 바꾸면 문구 뒤에 스페이스를 하나 붙여, 바로 이어서 쓸 수 있습니다.
- **0.13.1**: 편집 화면에서 고르기 전 안내 문구를 "입력창에 j, k 또는 프리셋 번호를 쳐서 고르면…"으로 다듬었습니다.
- **0.13.0**: 입력창에 `$번호` 뒤 스페이스를 쳐야 문구로 바뀌어 `$12`처럼 두 자리 번호도 쓸 수 있습니다. 스페이스 없이 바로 Enter를 눌러도 보낼 때 문구로 바꿉니다. 번호는 9개 제한 없이 모든 프리셋에 붙습니다. 펼친 프리셋은 한 줄로 보이고 넘치면 말줄임표로 자릅니다. 편집 화면은 vim처럼 `j`/`k`, `gg`/`G`, `J`/`K`, `i`, `o`, `dd`, `u`로 동작하고, 입력창 아래 안내 줄에 `-- NORMAL --`/`-- INSERT --`를 보입니다.
- **0.12.0**: 다른 세션에서 고친 프리셋을 열려 있는 세션에도 5초 안에 반영합니다.
- **0.11.2**: 두 줄로 넘친 문구에 마우스를 올리면 모든 줄이 함께 반전되도록 했습니다.
- **0.11.1**: 편집 화면 안내 문구와 저장 결과 메시지, 명령을 찾지 못했을 때의 알림 문구를 다듬었습니다.
- 모드 폴더를 저장소 최상위 `quick-replies/`에서 `mods/quick-replies/`로 옮겼습니다(버전 변화 없음).
- **0.11.0**: 편집 중에는 포커스를 입력창에 유지하고 키 입력을 가로챕니다. 쓰는 중에는 글자 색을 바꾸고 Enter로 저장하며, `$q`로 취소합니다.
- **0.10.0**: `$e`로 편집 화면을 열고 `$0`으로 접습니다. 빈 입력창에서 숫자 0 하나로 접던 단축키를 없앴습니다.
- **0.9.0**: 빈 입력창의 숫자 단축키 대신 입력창에 `$번호`를 쳐서 문구를 넣습니다. 띠의 번호 표시가 `$1:`이 됐습니다.
- **0.8.2**: 프리셋에만 접기 기능을 적용하고, 다음 제안은 항상 전체 문구로 표시합니다.
- **0.8.1**: `e` 키로 편집 화면을 엽니다.
- **0.8.0**: 긴 프리셋의 제목을 자동으로 생성합니다. 번호를 클릭하면 문구를 입력창에 넣고, 문구 자체를 클릭하면 바로 보냅니다.
- **0.7.0**: 다음 제안과 프리셋을 한 줄씩 접는 컴팩트 모드를 추가했습니다.
- **0.6.1**: 전역과 로컬 라벨을 추가하고, 프리셋을 한 줄에 하나씩 표시해 번호를 정렬했습니다. `/명령` 색도 번호와 같은 테마 색으로 바꿨습니다.
- **0.6.0**: 키 하나로 쓰는 편집 화면, 전역과 프로젝트 줄 분리, 스킬 명령 색.
- **0.5.0**: `/`로 시작하는 프리셋을 스킬이나 슬래시 명령으로 실행하도록 했습니다.
- **0.4.0**: 입력창 위로 돌아오고 전역과 프로젝트 프리셋을 나눴습니다. next-prompts와 입력창 위 틀을 나눠 씁니다.
- **0.3.0**: 버튼을 권한 모드 줄 아래 별도 줄로 옮겼습니다. dashboard와 입력창 아래 영역을 공유했습니다.
- **0.2.0**: 답장 버튼을 입력창 아래 줄로 옮겼습니다.
- **0.1.0**: 입력창 위에 자주 쓰는 답장 버튼을 두는 모드로 처음 추가했습니다.
