봇 명령과 화면
Telegram에서 aipager를 다루는 방법이에요. 입력하는 방법은 네 가지예요. 슬래시 명령, 키보드 버튼, Mini App 대시보드, 그리고 자유 메시지(글, 파일, 음성 메시지)예요.
봇은 설정된 채팅의 입력만 받아요. 신뢰 경계는 보안 모델을 보세요.
슬래시 명령#
이 명령들은 aipager/bot/lifecycle.py에 등록되어 있어요. Telegram의 명령 메뉴가 이것들을 보여 주고, aipager가 시작할 때와 세션이 바뀔 때마다 새로 고쳐져요.
| 명령 | 인수 | 하는 일 |
|---|---|---|
/start | - | 홈 화면: 이 채팅의 세션, 보통 메시지가 가는 곳(✍️ 메시지는 x1 세션으로 가요.), 그리고 🆕 새 세션, ⚙️ 설정, 이 채팅에 되살릴 수 있는 종료된 세션이 있으면 ↩️ 재개. 고정 키보드도 보여 줘요. |
/help | - | 할 일별로 정리한 짧은 안내(시작, 대화, 제어, 관리, 설정, 관리자에게는 /update)와, 명령을 누르면 바로 보내진다는 팁: 먼저 글을 덧붙이려면 길게 누르거나(휴대폰) Tab을 누르세요(컴퓨터). 설치 소유자(이 설치 자체의 봇과의 개인 채팅에 있는 사람)에게는 🐞 문제 신고 버튼도 있어요. 그 개인 채팅에서 문제 신고 보고서의 정확한 미리 보기를 보내기, 메모 추가, 취소와 함께 열어요. 보내기를 누르기 전까지 아무것도 보내지 않아요(보고서에 들어 있는 것). |
/app | - | Mini App 대시보드를 열어요(세션, diff 뷰어, 설정). |
/status | - | 이 채팅의 세션을 다룰 수 있는 목록으로 보여 줘요. 세션마다 자기 상태를 말로 알려 주고(⚙️ 작업 중 3:12 · Bash: run tests, ⏳ 답변 대기 중 · Bash: make deploy, 💤 준비됨), 보통 메시지가 갈 세션에는 ✍️가 붙고, 둘째 줄에는 모델, 컨텍스트, 비용, 대기 중인 것(대기열 3 (대기열 1개, 메모 2개)), 실행 중인 백그라운드 에이전트와 백그라운드 명령이 나와요. 세션마다 버튼 줄이 있어요. ✍️ x1은 다음 메시지들을 그 세션으로 보내고, 작업 중일 때는 ⏹ 중지, 답을 기다릴 때는 답하기, 나머지는 ⋮에 있어요. 아래에는 🆕 새 세션, 그리고 종료된 세션을 위한 ⚫ 종료됨 (n)이 있어요(재개, 삭제, 또는 목록에서 모두 지우기. /resume에는 남아 있어요). |
/stop [label] | 선택 | 세션의 지금 작업을 중단해요. 이름이 없으면 이 채팅에서 작업 중인 유일한 세션을 멈추고(내 메시지가 다른 세션으로 가고 있어도요. 백그라운드 에이전트나 백그라운드 명령이 아직 실행 중인 세션은 작업 중으로 쳐요), 여러 개면 선택기로 물어보고, 하나도 없으면 지금 진행 중인 작업이 없어요.라고 답해요. /stop x1(또는 /x1 stop)은 x1을 멈춰요(작업 중이 아니면 x1 세션은 지금 아무 작업도 하지 않아요.라고 답해요). 대기 중인 메시지도 버리고 몇 개를 버렸는지 알려 줘요. |
/now | - | Claude가 현재 세션의 대기열에 들고 있는 메시지를 지금 단계 다음이 아니라 바로 보내요(Claude Code 자체의 지금 보내기 단축키, Ctrl+X Ctrl+S). 언제든 쓸 수 있고, 채팅의 보내기가 멈췄거나 최소 모드여도 돼요. Claude가 들고 있는 것이 없으면 대기열에 기다리는 메시지가 없어요라고 답해요. 대기 중인 메시지를 지금 보내기를 보세요. |
/new [name] [first message] | 둘 다 선택 | 세션을 시작해요. /new x1은 바로 시작하고, /new x1 fix the tests는 “fix the tests”를 첫 메시지로 보내요. 아무것도 없이 보내면(예: 메뉴에서 누름) /new가 이름을 묻고, 다음에 보내는 메시지를 인수처럼 써서 /new 다음 x1 fix the tests도 결과가 같아요. 그룹에서는 카드에 이름으로 답장하세요(또는 봇 멘션: @aipagerbot x1). 거기서는 보통 메시지가 봇에 닿지 않으니까요. 바로 다음 메시지만이에요. 먼저 다른 일을 하면(사진, 다른 명령, 다른 메시지에 대한 답장, 키보드의 템플릿, 다른 버튼) 그것은 평소처럼 처리되고 카드는 닫혀요. /status, /settings, 키보드 메뉴는 카드를 열어 둬요. 이름을 묻는 카드는 세션이 받을 모드, 모델, 폴더도 보여 주고, 먼저 바꿀 수 있는 버튼이 함께 있어요. 어느 길로 가든 같은 “x1 준비 완료” 카드에 닿아요. 세션이 무엇을 받았는지, 다음 메시지가 그 세션으로 간다는 것, 다른 모드로 바꾸거나 모델을 바꾸는 한 번 누르기 버튼이에요. 모두의 새 세션은 자동 모드로 시작해요. /new가 쓰는 것은 /settings → 새 세션에서 바꾸세요. 이름 앞에 !를 붙이면 관리자(owner와 admin 역할), 또는 봇과의 자기 개인 채팅에서 역할이 메시지를 보내고 Claude에게 답할 수 있는 사람(user, read_only는 아님)에게는 세션이 전체 권한 모드로 시작해요. 다른 사람이라면 자동 모드로 시작하고 카드가 이유를 알려 줘요. 이미 쓰는 이름이라면 ↩️ 그 세션으로 가기(그 세션이 실행 중일 때) 또는 ♻️ 재개(종료됐을 때), 🆕 새 세션으로 교체, ↩️ 취소를 /new를 보낸 사람에게만 제안해요. 이름을 묻는 카드는 10분 동안 쓰지 않으면 만료돼요(⏱ 이 새 세션 카드는 만료됐어요. /new를 다시 보내세요.). 역할에 can_manage가 없는 멤버(user, read_only)는 채팅의 루트 폴더 안에서만 세션을 시작해요. 카드의 폴더가 루트 폴더이고, 폴더 페이지는 그것과 그 바로 안의 폴더를 보여 주며(“기본 폴더” 줄은 없어요), “새 폴더”는 고른 폴더 안에 폴더를 만들고, 루트 폴더 밖에서 작업하는 세션에 대한 “그 세션으로 가기”, “재개”, “새 세션으로 교체”는 모두 거부돼요(“이 세션은 내 폴더 ~/... 밖에서 작업해요. 그래서 지금 역할로는 이 세션을 재개하거나 재시작하거나 모드를 바꿀 수 없어요. 관리자에게 부탁하세요.”). |
/resume [label] | 선택 | 종료된 세션을 이름으로 재개하거나 선택기를 열어요. 세션은 쓰던 모드로 돌아와요. 전체 권한 모드였던 세션은 관리자나 자기 개인 채팅 안의 사람에게만 전체 권한 모드로 돌아오고, 다른 사람에게는 자동 모드로 돌아오며 답변이 알려 줘요(♻️ x1 세션을 자동 모드로 다시 열었어요: 전체 권한 모드는 관리자만 쓸 수 있어요.). 예전 aipager가 자기 예전 Auto 모드(아무 확인 없이 실행)로 저장한 세션은 전체 권한 모드로 돌아오고, Ask로 저장한 세션은 확인 모드로 돌아와요. 종료된 세션은 GONE_SESSION_MAX_AGE_DAYS일(기본값 14) 동안 목록에 남았다가 세션 목록을 떠나요. Claude의 저장된 대화는 건드리지 않아요. 역할에 can_manage가 없는 멤버는 폴더가 채팅의 루트 폴더 밖인 세션을 재개할 수 없어요(소유자와 관리자는 할 수 있어요). 재시작과 모든 모드 전환도 마찬가지이고, 확인 모드와 자동 모드 사이의 그 자리 전환도 포함돼요. |
/kill [label] | 선택 | 세션을 종료해요. 항상 확인을 거쳐요: ⏹ x1 세션을 종료할까요? Claude가 작업을 멈추고 세션이 닫혀요.와 [⏹ 종료] [취소]. 이름이 없으면 이 채팅에 실행 중인 세션이 하나뿐일 때 바로 확인으로 가고, 아니면 이 채팅의 실행 중인 세션 선택기를 보여 줘요. /status → ⋮ → ⏹ 세션 종료에도 있어요. |
/restart [label] | 선택 | 확인을 거쳐 세션을 재시작해요. Claude가 멈췄다가 다시 실행되고, 대화는 유지되며, aipager가 가장 최근에 그 세션을 시작했거나 바꾼 모드를 써요. 터미널에서 aipager session으로 시작한 세션은 그 뒤 Telegram에서 모드를 바꾸지 않았다면 확인 모드로 돌아와요(aipager는 터미널이 어떤 모드로 시작했는지 모르고, 더 넓은 모드를 짐작하는 일은 절대 없어요). Telegram에서 재개할 때도 마찬가지예요. |
/rename [label] [new] | 선택 | 세션에 새 이름을 줘요(종료된 세션도 돼요). /rename x1 newname은 바로 이름을 바꿔요. 단어가 더 적으면 물어봐요. 명령이 세션 하나만 가리킬 수 있으면 새 이름을 바로 묻고, 아니면 선택기를 보여 줘요(/rename 뒤에 단어가 하나뿐이면 세션을 고르지 않아요. 예전 이름일 수도, 새 이름일 수도 있으니까요). 그룹에서는 질문에 이름으로 답장하세요. |
/delete [label] | 선택 | 확인을 거쳐 종료된 세션을 목록에서 지워요. |
/diff [label] | 선택 | 세션 작업 폴더의 git diff를 보여 줘요. 이름이 없으면 내 메시지가 가고 있는 세션을 보여 줘요. |
/clearqueue | - | 현재 세션에서 아직 받지 않은 메시지를 모두 버려요(aipager가 보류 중인 메시지와 Claude 안에서 대기 중인 메시지 모두). 실행 중인 작업은 중단하지 않아요. 몇 개를 지웠는지 답해요. |
/mode [label] [ask|auto|full] | 선택 | 세션의 권한 모드와 다른 모드마다 바꾸는 버튼을 보여 줘요: ✍️ x1 세션은 🤖 자동 모드예요. [💬 확인 모드로 전환] [🔓 전체 권한 모드로 전환]. 모드는 세 가지예요. 💬 확인 모드: Claude가 작업마다 먼저 물어봐요. 🤖 자동 모드(기본값): Claude Code의 안전 검사가 안전한 작업은 실행하고 위험한 작업은 멈춰서 내가 정하게 해요. 🔓 전체 권한 모드: 모든 것이 아무 확인 없이 실행돼요(--dangerously-skip-permissions). 관리자, 또는 봇과의 자기 개인 채팅에서 역할이 메시지를 보내고 Claude에게 답할 수 있는 사람(user, read_only는 아님)만 고를 수 있어요. 자동 모드와 전체 권한 모드는 Claude Code 자체의 권한 요청만 바꿔요. aipager의 hook은 여전히 멤버의 역할 규칙을 적용해요. /mode ask, /mode auto, /mode full, /mode x1 ask는 바로 바꿔요. 채팅 언어로 된 모드 단어 자체도 “모드”라는 말 없이 쓸 수 있어요(💬 확인 모드는 /mode x1 확인, 🔓 전체 권한 모드는 /mode x1 전체). 이 채팅에서 실행 중인 세션이 정확히 그 이름이면(예: demander라는 세션) 그 단어는 세션 이름이에요. /mode demander는 그 세션의 모드를 보여 주고, /mode x1 demander는 여전히 x1을 바꿔요. ask, auto, full은 순서와 상관없이 항상 모드예요(/mode x1 ask 또는 /mode ask x1). 두 단어가 모두 모드 단어이고 정확히 하나가 실행 중인 세션의 이름일 때만 그 단어가 세션 이름이에요. 그래서 full이라는 세션에 대해 /mode full ask와 /mode ask full은 둘 다 그 세션을 확인 모드로 바꾸고, /mode full만 쓰면 여전히 모드예요. 확인 모드와 자동 모드 사이의 전환은 재시작 없이 그 자리에서, 언제든(Claude가 작업 중이어도) 일어나요. aipager가 내가 하듯이 세션에서 Shift+Tab을 누르고, Claude Code가 다음 단계에서 새 모드를 알려 줄 때까지 카드에 ⚙️ x1 세션을 재시작 없이 🤖 자동 모드로 바꾸는 중이에요.가 나왔다가, 결과를 보여 줘요. 그 세션에서 자동 모드를 쓸 수 없으면(모델이나 요금제 때문에) 카드가 알려 주고 세션은 확인 모드에 남아요. 세션이 질문이나 권한 요청을 보여 주는 동안에는 바꿀 수 없어요. 먼저 답하세요. 전체 권한 모드로 바꾸려면 관리자(또는 자기 개인 채팅 안의 사람)와 확인이 필요하고, 세션을 재시작해요(대화는 유지돼요). 전체 권한 모드에서 나올 때도 바로 재시작해요. 전체 권한 모드로 시작한 세션은 나중에 터미널에서 확인 모드나 자동 모드로 바꿨더라도 전환할 때마다 재시작하고, 시작한 뒤 전체 권한 모드를 알려 온 세션(--dangerously-skip-permissions로 시작한 터미널 세션)도 그래요. 거기서는 Shift+Tab이 다시 전체 권한 모드로 돌아갈 수 있으니까요. 바쁜 세션이라면 재시작에 🛑 중지 후 모드 전환 / ⏳ 나중에를 제안해요. 역할에 can_manage가 없는 멤버는 폴더가 채팅의 루트 폴더 밖인 세션의 모드를, 그 자리 전환이든 재시작이든 바꿀 수 없어요. 전환 버튼을 누르면 메시지를 하나 더 보내는 대신 그 카드 자체를 바꿔요. /perms는 예전 이름이고 여전히 똑같이 쓸 수 있어요. |
/settings | - | 이 채팅의 세션이 어떻게 보이고 어떻게 답하는지: aipager가 쓰는 언어, 메시지 배치, 변경 사항 미리 보기, 긴 작업의 카드 업데이트, 서식, 답변 길이, 어휘 수준. /new가 세션을 무엇으로 시작하는지. 세션별 설정. 그리고 소유자의 개인 채팅에서는 문제 신고 보고서를 보내자는 제안. 설정을 보세요. 그룹에서는 누구나 볼 수 있고, 설정을 바꾸려면 관리자가 필요해요. |
/language | - | 이 채팅에서 aipager가 자기 메시지와 버튼에 쓰는 언어예요(/settings 첫 줄과 같은 목록). 언어마다 자기 이름으로 나와요. English, 그다음 Español(스페인어), Русский(러시아어), Português (Brasil)(브라질 포르투갈어), 简体中文(중국어 간체), Français(프랑스어), Deutsch(독일어), 한국어, العربية(아랍어), Bahasa Indonesia(인도네시아어), Українська(우크라이나어), Italiano(이탈리아어), Türkçe(튀르키예어), فارسی(페르시아어), Tiếng Việt(베트남어)예요. 이 버전 뒤에 시작한 개인 채팅은 내 Telegram 앱의 언어를 따르고, 그전부터 있던 채팅은 내가 고를 때까지 영어를 써요(언어 참고). 스페인어, 포르투갈어, 프랑스어, 이탈리아어, 인도네시아어에서는 답장 키보드의 단어가 명령과 같은 status, stop, new, kill로 남아요. 독일어와 튀르키예어는 Status, Stop, Neu…, Beenden…과 Özet, Stop, Oluştur, Sonlandır를 써요. 영어 단어를 입력해도 여전히 돼요. 페르시아어와 아랍어에서는 aipager의 글이 오른쪽에서 왼쪽으로 읽히고 페르시아 숫자나 아랍 숫자를 써요. 세션 이름, 명령, 모델 이름, 코드는 그대로예요. 목록은 무엇이 바뀌는지 알려 줘요. 여기와 Mini App의 aipager 자체 글만 바뀌고, 내 Claude 세션은 바뀌지 않으며, Claude는 내가 쓴 언어로 답해요. 그룹에서는 누구나 볼 수 있고, 바꾸려면 관리자가 필요해요. language는 예약된 세션 이름이에요. |
/whoami | - | 여기서 내가 누구인지 알려 줘요. 팀 모드에서는 내 라벨, 이 채팅에서의 내 역할, 그 역할이 나에게 적용하는 규칙이에요. |
/update | - | 관리자 전용(owner와 admin 역할, 또는 can_manage가 있는 역할). aipager와 Claude Code에 더 새 버전이 있는지 확인하고, 있는 쪽을 버튼 하나로 업데이트해요. 업데이트를 보세요. |
/ 메뉴는 먼저 내 세션을 보여 주고(/x1 · x1 세션과 대화), 그다음 자주 쓰는 명령이에요. new, status, stop, now, resume, mode, settings, language, help(Mini App이 켜져 있으면 app도, 개인 채팅에서만). kill, restart, rename, delete, diff, clearqueue, perms, whoami, update는 메뉴에 없지만 입력하면 여전히 돼요. 세션에 대한 작업은 /status → ⋮에도 있어요.
세션 하나에 작용하는 명령(/stop, /mode, /kill, /restart, /rename, /delete, /diff)은 한 가지 규칙을 따라요:
- 이름이 있으면(
/restart x1) 이 채팅의 그 세션에 작용해요(/rename은 이를 위해 예전 이름과 새 이름이 둘 다 필요해요. 위 참고). - 이름이 없으면 명령이 가리킬 수 있는 유일한 세션에 작용하고(
/stop이면 작업 중인 유일한 세션,/kill과/restart면 실행 중인 유일한 세션,/delete면 종료된 유일한 세션), 평소 확인 단계가 있으면 거쳐요. - 그렇지 않으면 이 채팅에서 그 명령에 맞는 세션만 담은 선택기를 보여 줘요. 내 메시지가 가고 있는 세션이
✍️표시와 함께 맨 앞에 오고, 나머지 세션은 이름순이며,✖️ 취소가 있어요. 세션을 고르면 그 이름을 입력했을 때와 같은 카드로 가요. - 그룹에서 이름이 없으면 명령이 내 현재 세션에 맞을 때만 그 세션에 작용하고, 그렇지 않으면 세션이 하나뿐이어도 항상 선택기를 보여 줘요. 그 세션이 다른 멤버의 것일 수 있으니까요(그룹 참고).
/mode와 /diff는 먼저 내 메시지가 가고 있는 세션을 봐요.
설정#
/settings(와 같은 선택지를 보여 주는 Mini App의 설정 탭)는 이 채팅의 세션이 어떻게 보이고 답하는지 정해요. 채팅마다 자기 설정이 있어요. 그룹에서는 누구나 볼 수 있고, 설정을 바꾸려면 관리자가 필요해요. 내 개인 채팅에서는 역할과 상관없이 내가 직접, Telegram과 Mini App 모두에서 바꿔요. ✅가 붙은 줄은 기본값과 다르게 설정된 줄이에요(메시지 배치는 항상 선택을 보여 줘요).
| 설정 | 선택지(기본값이 먼저) | 하는 일 |
|---|---|---|
| 🌐 언어 | 자동 (내 Telegram 언어), 개인 채팅에서; English | 이 채팅과 Mini App에서 aipager가 자기 메시지와 버튼에 쓰는 언어로, 자기 이름으로 보여요(/language와 같은 목록). 내 Claude 세션은 바꾸지 않아요. Claude는 내가 쓴 언어로 답해요. English, Español(스페인어), Русский(러시아어), Português (Brasil)(브라질 포르투갈어), 简体中文(중국어 간체), Français(프랑스어), Deutsch(독일어), 한국어, العربية(아랍어), Bahasa Indonesia(인도네시아어), Українська(우크라이나어), Italiano(이탈리아어), Türkçe(튀르키예어), فارسی(페르시아어), Tiếng Việt(베트남어)가 있어요. 자동은 내 Telegram 앱의 언어를 따라요(언어 참고). |
| 🖼 메시지 배치 | 작업 카드 + 따로 오는 답변; 답변을 카드 안에; 카드 지우고 답변만 | 끝난 작업이 어떻게 보이는지. “작업 카드 + 따로 오는 답변”은 답변 위에 카드를 남겨서 답에 이르기까지의 기록으로 둬요. “답변을 카드 안에”는 답변을 카드에 넣어요. “카드 지우고 답변만”은 카드를 지우고 답변만 남겨요. 기본값은 “작업 카드 + 따로 오는 답변”이에요(한 번도 고른 적 없는 채팅에서는 KEEP_FINISHED_CARD=0이 “카드 지우고 답변만”으로 정해요). 작업이 끝났을 때의 답변을 보세요. |
| 📝 변경 사항 미리 보기 (diff) | 끄기; 켜기 | 켜면 Write/Edit마다 작업 카드 아래에 별도의 diff 메시지로 올려요. 끄면 카드 하나와 답변 하나로 유지해요. 카드에는 여전히 모든 편집이 나오고, Mini App에도 diff 뷰어가 있어요. |
| ⏱ 긴 작업의 카드 업데이트 | 켜기; 끄기 | 켜면 작업 2분 뒤부터 작업 카드가 10초마다, 10분 뒤부터 30초마다 새로 고쳐지고(작업이 아무리 길어도 이보다 느려지지 않아요), 그때 시계는 30초 단위로 움직여요. 끄면 작업 내내 처음 몇 분의 속도를 유지해요(문제 해결 참고). |
| ✏️ 간단한 서식 | 끄기; 켜기 | 켜면 Claude에게 단순한 문장과 글머리 목록만 쓰라고 해요. 표, 코드 블록, 제목, 굵은 글씨가 없어요. |
| 📏 답변 길이 | 규칙 없음; 아주 짧게; 짧게; 중간; 길게 | Claude에게 그 길이로 답하라고 해요. |
| 🎓 어휘 수준 | 규칙 없음; 쉬운 단어; 보통; 고급 | Claude에게 그 수준의 어휘를 쓰라고 해요. |
| 🎨 모양 | 중지 버튼, 허용·거부 버튼, App 버튼, 설정 메뉴 | 이 채팅에서 aipager 버튼이 보이는 방식이에요. 처음에는 모두 기본(Telegram 자체 모양)이에요. Telegram은 봇이 버튼을 파란색, 초록색, 빨간색으로만 칠하게 해 줘서, 중지 버튼(작업 카드의 버튼과 질문이나 권한 요청 아래 버튼)은 기본, 파란색, 초록색, 빨간색, 허용·거부는 기본 또는 색 있음(허용과 항상 허용은 초록, 거부는 빨강), App 버튼(고정 메시지와 /app 아래)은 기본, 파란색, 초록색, 빨간색(메시지 입력창 옆의 메뉴 버튼은 칠할 수 없어요), 설정 메뉴는 기본(✅가 지금 선택한 항목을 표시해요)과 지금 선택한 항목을 파란색으로도 칠하는 것 중에서 고를 수 있어요. 세션별이 아니라 채팅 전체에 적용되고, 그룹에서는 관리자만 바꿀 수 있어요. 색을 지원하지 않는 Telegram 앱에서는 모든 버튼이 기본 모양으로 보여요. Mini App의 설정 탭에도 채팅 설정 다음에 따로 있는 모양 부분에 같은 선택지가 있고, 저마다 색 점이 붙어 있어요. |
| 🆕 새 세션 | 권한(모드), 모델, 폴더 | 내가 고르지 않을 때 /new가 세션을 무엇으로 시작하는지: 확인 모드, 자동 모드(기본값), 전체 권한 모드, 모델 목록의 모델이나 Claude Code의 기본 모델, 그리고 폴더예요. 여기서 전체 권한 모드를 고를 수 있는 사람은 관리자, 또는 봇과의 자기 개인 채팅 안의 사람뿐이고(개인 채팅 화면은 다른 사람에게 이것을 보여 주지 않아요), 쓸 수 없는 사람에게는 절대 적용되지 않아요. 그 사람의 세션은 자동 모드로 시작해요. |
| 👤 세션별 설정 | 세션 하나, 그다음 새 세션을 뺀 위의 모든 설정 | 세션 하나에만 따로 정하는 값이에요(/status → ⋮ → 👤 세션 설정에도 있어요). ⭐는 따로 정한 설정을 표시하고, “채팅 기본값 사용”은 따로 정한 것을 지워요. Mini App의 세션 페이지도 같은 값을 정해요. |
| 🐞 문제 신고 | 물어보기; 끄기 | 설치 소유자의 개인 채팅에만 있어요. 물어보기는 aipager가 여러 번 겪은 내부 오류에 대해 가끔 보고서를 보내자고 제안하게 해요(최대 3일에 한 번). 끄기는 절대 제안하지 않아요(/help의 “문제 신고” 버튼은 여전히 쓸 수 있어요). 제안을 연달아 두 번 거절하면 제안이 꺼지고, 그러면 이 줄이 그렇다고 알려 주며 다시 켜는 버튼이 함께 와요. 문제 신고를 보세요. |
답변 길이, 어휘 수준, 간단한 서식은 Telegram에서 보낸 메시지마다 덧붙는 짧은 지시로 Claude에게 가고(hook이 붙여요. hook 참고), 세션에 따로 정한 값이 있으면 그것을 써요.
배치와 상관없이 모든 작업 카드는 세션의 상태 줄(⏳/✅ 이름 · …)로 끝나고, 모든 답변은 결과 줄(💬 이름, 숫자를 보여 줄 끝난 카드가 없으면 · 완료 (…)를 더해요)로 시작해요. “답변을 카드 안에” 배치는 두 줄을 위아래로 쌓고, 각자 자기 부분에 둬요. 작업 카드의 상태 줄 위에는 구분선이 있고(작은 글씨), “답변을 카드 안에” 배치에서는 두 번째 구분선이 끝난 카드와 답변을 나눠요. 카드가 있는 배치에서는 답변이 일부러 끝난 카드보다 조금 늦게 와서, 카드에 완료가 나온 것을 먼저 보고 그 아래에 답변이 나타나는 것을 보게 해요. FINISH_CARD_GRACE_SECONDS로 이 지연을 조정하거나 끄세요(초, 기본값 0.8, 0이면 둘을 함께 보내요). 카드가 있는 배치에서 도구를 하나도 호출하지 않은 작업은 카드를 남기지 않아요. 카드가 완료로 바뀌기 전에 지워지고, “카드 지우고 답변만” 배치처럼 답변의 첫 줄이 숫자를 보여 줘요.
세션마다의 명령#
실행 중인 세션마다 라벨로 등록된 명령이 하나 있어요:
| 형태 | 하는 일 |
|---|---|
/<label> | <label>을 내 메시지가 갈 세션으로 만들어요. 답변이 그렇다고 알려 주면서 세션의 상태와 모드를 함께 보여 주고(✍️ 이제 x1 세션과 대화해요 · 💤 준비됨 · 🤖 자동 모드, 그다음 “메시지를 보내면 x1 세션으로 가요.”), 세션이 답을 기다리면 답하기를, 메뉴를 여는 ⋮ 더 보기를 제안해요. 전체 숫자는 /status에 있어요. 그룹에서는 대신 “x1 세션의 메시지에 답장하면 (또는 @aipagerbot 멘션) 그 세션과 대화할 수 있어요.”라고 해요. 거기서는 보통 메시지가 봇에 닿지 않으니까요(그룹 참고). 그룹 메뉴에서 /x1을 고를 때 Telegram이 보내는 /x1@aipagerbot도 똑같이 동작해요. |
/<label> <prompt> | <prompt>를 그 세션으로 바로 보내요. 그 세션은 다음 보통 메시지들이 갈 세션도 돼요. |
/<label> stop | 그 세션의 지금 작업을 중단해요. |
/status의 결과는 aipager status가 명령줄에 보여 주는 것과 같은 데이터에서 와요. 세션 목록 자체는 Telegram을 호출할 필요가 없어요.
컴퓨터에서 aipager status와 aipager session ls는 세션마다 상태를 하나 줘요. IDLE, BUSY, WAITING(권한 요청이나 질문이 나를 기다리고 있고, 알면 내용도 함께 나와요. 예: permission: Bash: make deploy), GONE이에요. --json이면 세션마다 waiting_kind(permission 또는 question)와 waiting_summary도 있고, 세션이 기다리는 중이 아니면 둘 다 null이에요.
언어#
aipager는 English, Español, Русский, Português (Brasil), 简体中文, Français, Deutsch, 한국어, العربية, Bahasa Indonesia, Українська, Italiano, Türkçe, فارسی, Tiếng Việt로 말하고, 언어마다 자기 이름으로 나와요.
- aipager 자체의 글만 바뀌어요. 언어는 aipager의 메시지, 버튼, 카드, Mini App에 적용돼요. 내 Claude 세션은 바뀌지 않고, Claude는 내가 쓴 언어로 답해요. 세션 이름, 명령, 모델 이름, 코드는 그대로예요.
/language(또는/settings의 첫 줄)는 내가 있는 채팅의 언어를 골라요. 개인 채팅에서는 그것이 내 언어이기도 해요. 그룹에서 보는 짧은 안내와 내 Mini App이 그 언어를 써요.- 자동(개인 채팅에서만)은 내 Telegram 앱의 언어를 따르고, 바꾸면 다시 따라가요. aipager에 없는 언어는 영어로 보여요.
- 새 채팅과 예전 채팅. 이 버전 뒤에 시작한 개인 채팅은 내가 언어를 고를 때까지 내 Telegram 앱의 언어를 따르고, 영어가 아닌 첫 메시지에는 그렇다는 것과 바꾸는 방법을 알려 주는 짧은 줄이 붙어요. 그전부터 있던 채팅은 영어를 유지해요. 내 Telegram 앱이 aipager가 말하는 언어를 쓰면 그 언어를 제안하는 카드가
영어 유지버튼과 함께 와요. 어떻게 답하든 한 번만 와요. - 그룹은 언어가 하나이고, 관리자가
/language에서 정해요. 그때까지 그룹은 영어예요. 누른 뒤의 짧은 안내는 누른 사람 자신의 언어로 나와요. 봇과의 개인 채팅에서 고른 것, 없으면 이 버전 뒤에 들어온 사람이라면 Telegram 앱의 언어, 그것도 없으면 그룹의 언어예요. 그룹을 보세요. - 오른쪽에서 왼쪽. 페르시아어와 아랍어에서는 aipager의 글이 오른쪽에서 왼쪽으로 읽히고 페르시아 숫자나 아랍 숫자를 써요. 작업 카드는 Claude가 어떤 언어로 쓰든 채팅의 언어를 따라가요. 영어 채팅에서는 왼쪽에서 오른쪽으로, 페르시아어나 아랍어 채팅에서는 오른쪽에서 왼쪽으로 읽히고, 카드 위에 반대 방향으로 쓰인 Claude의 문장은 그대로 자기 방향으로 읽혀요. 답변은 Claude의 글을 따라가요. 왼쪽에서 오른쪽으로 쓰는 언어의 채팅에서 Claude가 페르시아어나 아랍어로 답하면 답변 전체가 오른쪽에서 왼쪽으로 쓰는 메시지가 돼요. 그 안의 aipager 자체 줄(답변 아래 에이전트, “답변을 카드 안에”에서는 그 위의 끝난 카드)도 나머지처럼 오른쪽에 놓이지만, 여전히 왼쪽에서 오른쪽으로 읽혀요.
- 모드 단어.
/mode는 내 언어의 모드 단어를 받고(/mode 확인),ask,auto,full은 항상 돼요. 실행 중인 세션이 정확히 그 이름이면 그 단어는 세션 이름이에요(위의/mode참고). - 봇 설명. 시작할 때 aipager는 이 언어마다 봇 설명(빈 채팅 화면의 글)과 짧은 설명을 써서, Telegram이 그 언어인 사람들에게 보여 줘요. Telegram은 내가 BotFather에서 쓴 글 대신 이 글을 보여 주니까, 내 글이 이미 있으면 aipager는 아무것도 설정하지 않아요(설명과 짧은 설명을 따로 확인해요). 내 글을 절대 덮어쓰지 않고, 영어 설명이나 기본 설명을 절대 바꾸지 않아요. 지금 글이 모든 언어에 이미 설정되어 있으면 시작할 때 Telegram에 아무것도 묻지 않아요. 아니면 먼저 BotFather의 내 글을 읽어요(설명에 한 번, 짧은 설명에 한 번). 내 글이 있으면 거기서 멈추니까, 내 글이 있을 때는 시작할 때마다 그 읽기만 하고 더는 하지 않아요. 없으면 아직 자기 지금 글이 없는 언어를 하나씩 읽으니까, 내가 직접 글을 쓴 언어는 시작할 때마다 다시 읽혀요. aipager가 자기 설명을 설정한 뒤에 내가 BotFather에서 설명을 쓰면, 이 언어로 읽는 사람들은 여전히 aipager의 설명을 봐요(문제 해결 참고).
Mini App#
/app(과 Telegram의 메뉴 버튼)은 aipager가 직접 제공하는 대시보드를 열어요. 기본으로 켜져 있고, aipager miniapp enable|disable|status로 관리해요. 모든 요청은 Telegram의 initData 서명으로 확인해요(보안 → Mini App 터널 참고). 페이지는 다른 곳에서 아무것도 불러오지 않아요. 다른 사이트의 글꼴, 이미지, 스크립트가 없어요.
- 채팅. 앱은 한 번에 채팅 하나를 보여 줘요. 여러 채팅에 속해 있으면(예: 내 개인 채팅과 그룹) 맨 위의 채팅 이름 줄로 오갈 수 있어요. 앱은 내 개인 채팅으로 열리고, 그 휴대폰에서 마지막 선택을 기억해요. 아래의 모든 것은 고른 채팅의 것이고, 그 채팅에서의 내 역할을 써요. 채팅이 하나뿐이면 이 줄은 나오지 않아요. 그룹을 보세요.
- 세션. “답변 대기 중 1개, 작업 중 2개, 준비됨 3개” 같은 채팅 요약 한 줄과 그 아래 쓴 금액이 있어요. 실행 중인 세션마다 컨텍스트 링, 폴더, 모델, 비용이 있는 타일이 하나씩 있고, 작업 중인 세션은 강조색으로 빛나요. 끝난 세션은 접을 수 있는 종료됨 선반에 있어요. + 버튼은 새 세션을 시작해요.
- 답변 대기 중. 권한 요청이나 질문을 기다리는 세션은 맨 위의 호박색 트레이에 놓이고, 무엇을 묻는지 보여 줘요. 채팅에서 답하기는 그 요청을 버튼과 함께 채팅 맨 아래로 다시 보내요. 고정된 표시줄의 답하기 버튼처럼요. 그리고 거기서 답해요. 기다리는 세션이 그것뿐이면 앱이 닫혀서 요청이 바로 눈앞에 와요. 아니면 열린 채로 “채팅에 보냈어요”를 보여 줘요. 이것은 세션에 메시지를 보낼 권한이 필요하고, 요청이 이미 답해졌거나 바뀐 뒤에 누른 사본은 “이미 답했어요”를 보여 줘요.
- 세션 페이지. 머리 부분은 컨텍스트 링, 상태(“작업 중 (3:12)”, “답변 대기 중 (권한)”, “준비됨, 마지막 활동 5분 전”, “종료됨”), 그리고 한 번 누르는 중지(작업 중이거나 기다릴 때) 또는 재개(종료됐을 때)를 보여 줘요. 아래에는 기다리는 요청과 채팅에서 답하기, 최근 도구 호출 몇 개를 한 줄로(누르면 전체 타임라인), 최신 답변, 정보, 모델 선택, 세션 자체 설정, 바뀐 파일, 타임라인이 있어요. ⋮ 메뉴에는 모든 작업이 있고, 확인 단계는 예전과 같아요.
- 새 세션. 단계별로 안내하는 카드예요. 이름, 그다음 위치(내 세션이 쓴 폴더의 최근 제안, 최신순), 그다음 모델(추천 제안), 그다음 권한 모드와 답변 스타일을 위한 옵션 더 보기예요. 아래쪽의 Telegram 자체 버튼에는 세션 시작이라고 적혀 있어요. 역할에
can_manage가 없는 멤버에게는 “위치”가 채팅의 루트 폴더와 그 바로 안의 폴더만 제안하고(루트 폴더가 기본값이고, “새 폴더”는 고른 폴더 안에 폴더를 만들어요), 밖의 폴더는 이유와 함께 거부되며, 그 밖에서 작업하는 세션의 재개, 재시작, 모드 전환은 모두 “이 세션은 내 폴더 ~/... 밖에서 작업해요. 그래서 지금 역할로는 이 세션을 재개하거나 재시작하거나 모드를 바꿀 수 없어요. 관리자에게 부탁하세요.”라고 답해요. - 설정. 맨 앞은 앱 언어예요. 앱과 내 개인 채팅에서 aipager 글에 쓰는 나만의 언어예요(영어, 스페인어, 러시아어, 포르투갈어(브라질), 중국어(간체), 프랑스어, 독일어, 한국어, 아랍어, 인도네시아어, 우크라이나어, 이탈리아어, 튀르키예어, 페르시아어, 베트남어. 자동은 내 Telegram 앱의 언어를 따라요. 언어 참고). 누구나 자기 것을 바꿀 수 있고, 채팅에 올라가지 않으며, 내 Claude 세션을 바꾸지 않아요. 그다음 채팅의 설정(그룹에서는 관리자만 바꿀 수 있고, 내 개인 채팅에서는 거기의
/settings처럼 역할과 상관없이 내가 바꿀 수 있어요), 그리고 관리자에게는/update와 같은 업데이트가 있어요. 자기 개인 채팅의 소유자에게는 문제 신고가 별도 페이지를 열어요. 메모를 쓰고, 보고서를 확인하고, 보내고, 참조 번호를 보는 것을 앱을 떠나지 않고 할 수 있어요(문제 신고 참고).
앱은 Telegram의 밝은 테마나 어두운 테마가 바뀌면 따라가고, 휴대폰이 움직임 줄이기를 요청하면 가벼운 애니메이션을 멈춰요. Mini App에서 할 수 있는 모든 것은 채팅에서도 할 수 있어요. 세션 패널의 ⋮ 메뉴에 같은 작업이 있어요.
실행 중인 세션의 모델 바꾸기#
실행 중인 세션의 페이지에는 모델 선택이 있어요. 세션이 알려 오는 모델(Claude Code의 상태 줄에서)과, 시작할 때의 선택기 및 채팅의 모델 키보드와 같은 목록을 보여 줘요. 모델을 고르면 그 세션에 정확히 /model <name>을 입력해요. 채팅의 모델 키보드가 쓰는 입력 방식과 같고, 누가 할 수 있는지의 규칙도 같아요(그 세션에 메시지를 보낼 수 있는 사람 누구나). 상태 줄이 다른 모델을 알려 올 때까지 선택에 바꾸는 중… 표시가 나오고, 그다음 그 모델을 보여 줘요. 15초 안에 아무것도 바뀌지 않으면 확인 안 됨 (세션을 확인하세요) 표시가 나와요.
- 세션이 준비됐을 때만이고, Claude가 작업 중이거나 열린 요청이 있을 때는 안 돼요. Claude Code는
/model을 작업이 끝난 뒤가 아니라 작업 중간에도 바로 실행하니까, 작업이 끝날 때까지 변경을 거부해요. 채팅의 키보드도 같은 상태에서 같은 말로 거부해요. - Claude Code의 “Switch model?” 질문. Claude Code는 보통 바꾸기 전에 이것을 물어요. 다음 답변이 대화 전체를 다시 읽어야 하니까요. Claude Code 2.1.251 이상에서는 aipager의
PreModelSwitchhook이 aipager가 방금 입력한 변경을 승인해서 이 질문이 나오지 않아요(hook 참고). 다른 모든 변경은 여전히 물어봐요. 더 오래된 Claude Code에서, 또는opusplan으로 바꾸거나 거기서 다른 모델로 바꿀 때는 이 질문이 여전히 나올 수 있어요. 그러면 변경이 확인 안 됨으로 보이고 질문이 터미널에서 기다려요. 세션에 무엇이든 더 보내기 전에 거기서 먼저 답하세요. 그렇지 않으면 다음 메시지가 그 질문에 입력돼요. 같은 이유로 aipager는 확인되지 않은 변경 뒤 1분 동안, 또는 세션이 새 모델을 알려 올 때까지 그 세션의 다음 변경을 거부해요.
고정된 상태 표시줄#
aipager가 대화하는 모든 채팅(내 개인 채팅과 모든 그룹)에는 고정 메시지가 하나 있어요. 상태 표시줄이에요. 채팅 맨 위의 Telegram 고정 띠는 이 메시지를 첫 줄부터 줄을 이어 붙여 보여 주니까, 첫 줄에는 나에게 가장 필요한 것이 적혀요:
| 첫 줄 | 언제 |
|---|---|
⏳ jim 세션이 답변을 기다려요 - Bash: make deploy | 세션이 권한 요청, 질문, 또는 다른 대화 상자의 답을 기다리고 있어요. 다른 세션도 기다리고 있으면 (+2개 더) |
⚙️ jim (작업 중) / 💤 jim (준비됨) | 채팅에 실행 중인 세션이 하나 있고 작업 중 / 준비됨이에요(시작하는 동안에는 🔄 jim (시작 중)). 속도 제한에 관한 한 줄을 빼면 이 줄이 상태 표시줄 전부예요 |
⚙️ 작업 중 2개 · 준비됨 1개 | 세션이 여럿이고 기다리는 세션은 없어요. 상태마다(작업 중, 준비됨, 시작 중) 세션이 몇 개인지. 작업 중인 세션이 없고 시작 중인 세션이 있으면 🔄, 모두 준비됨이면 💤 |
💤 모두 준비됨 | 실행 중인 세션이 없어요 |
그 아래에는, 해당되는 동안에만, 속도 제한에 관한 줄이 하나 있어요(🐢 Telegram 경고 후 천천히 보내는 중(429 뒤 6시간 동안) 또는 ⏸ 카드 업데이트 잠시 멈춤 (Telegram 시간당 한도) / (Telegram 제한)(최소 모드, 문제 해결 참고)). 그다음, 채팅에 실행 중인 세션이 둘 이상이면 ✍️ 메시지는 jim 세션으로 가요(이 채팅에서 보통 메시지가 갈 세션, 아래 참고. 사람마다 대상이 따로 있는 그룹에는 없어요)와, 세션마다 상태가 붙은 줄이 하나씩 있어요. 작업 중, 답변 대기 중, 준비됨, 시작 중이에요. 상태 표시줄은 세션마다 이름을 한 번만 적어요. 첫 줄이 이미 이름을 적은 기다리는 세션은 자기 줄이 없고, 첫 줄은 아래 줄들이 되풀이할 이름을 절대 나열하지 않아요. 아직 백그라운드 에이전트가 있는 세션은 자기 줄에(세션이 하나뿐이면 그 유일한 줄에) 개수를 적어요: 💤 jim (준비됨, 에이전트 1개 실행 중). Claude의 백그라운드 명령도 같은 방식으로 세어요: (준비됨, 백그라운드 명령 1개 실행 중), 둘 다 있으면 (준비됨, 에이전트 2개, 백그라운드 명령 1개 실행 중). 항상 숫자이고 이름은 절대 아니라서, 표시줄은 숫자가 바뀔 때만 바뀌어요. 표시줄을 누르면 그 메시지로 가요.
보통 메시지는 어디로 가나요. 답장이 아닌 메시지는 이 채팅이 가장 최근에 대화한 세션으로 가요. 가장 최근에 /<label>로 전환했거나, 메시지를 보냈거나, 시작했거나, 방금 여기서 나에게 답한 세션이에요. 이름 없는 /mode, /clearqueue, /diff, /now도 그 세션에 작용해요(/stop은 작업 중인 세션을 멈춰요). 채팅마다 자기 대상이 있어요. 한 채팅의 메시지는 다른 채팅에 속한 세션으로 절대 가지 않아요. 그룹에서는 봇에 대한 답장, 봇을 멘션한 메시지, 명령, 그룹 키보드의 누름만 세고(그룹 참고), 사람마다 자기 대상이 있으며 세션의 답변 때문에 바뀌지 않아요(그룹 참고).
고정 메시지의 버튼:
- <label> 답하기(기다리는 세션마다 하나, 최대 셋)는 그 세션의 요청을 답하기 버튼과 함께 채팅 맨 아래로 다시 보내서, 스크롤하지 않고 답할 수 있게 해요. 그사이 요청이 이미 답해졌으면 “이미 답했어요”라는 짧은 안내를 받고, 요청이 다른 곳에서 답해진 뒤에 누른 사본(또는 원래 요청 자체)도 똑같이 거부돼요. 나중에 온 요청에 답하는 일은 절대 없어요. aipager가 재시작한 뒤에도 기다리는 요청은 작업 카드나 자기 메시지에서 여전히 답할 수 있지만, aipager는 예전 사본이 어떤 요청을 보여 줬는지 더는 모르니까, 재시작 전에 표시줄이 다시 보낸 사본을 누르면 “이 요청은 이제 열려 있지 않아요”로 거부돼요. 요청에 답할 수 있는 사람은 이 버튼을 쓸 수 있고, 다른 사람은 쓸 수 없어요.
- 📱 App은 Mini App이 실행 중인 동안 내 개인 채팅에서 Mini App을 열어요.
표시줄에는 시계, 비용, 컨텍스트 %, 모델이 없어서 세션의 상태가 바뀔 때만 바뀌어요. aipager는 보여 주는 내용이 바뀌었을 때만 표시줄을 편집하고(조용히, 알림 없이), 채팅마다 최대 30초에 한 번이에요(PINNED_MIN_EDIT_GAP). 그 간격 안의 변경은 간격이 끝날 때 보여 주고, 절대 잃어버리지 않아요. 채팅의 보내기가 멈춘 동안에는 절대 편집하지 않고, 보내기 멈춤이 풀리면 밀린 것을 따라잡아요.
그룹에서 봇이 고정하려면 관리자 권한이 필요해요. 고정이 허용되지 않으면 aipager는 방금 보낸 상태 메시지를 지우고(고정되지 않은 채 그룹 기록에 남지 않게요), aipager가 재시작할 때까지 그 그룹에 상태 표시줄을 보여 주지 않아요. 봇을 완전히 거부하는 채팅(봇을 내보내거나 차단)도 마찬가지예요. 고정 메시지를 지우면 aipager가 새 메시지를 보내 고정하고, 채팅마다 최대 한 시간에 한 번이에요.
고정 키보드#
고정 키보드가 채팅의 입력란 아래에 있어요. 줄은 위에서부터:
| 줄 | 버튼 | 참고 |
|---|---|---|
| 세션 | 실행 중인 세션 라벨마다 하나 | 세션 목록에서 자동으로 만들어요 |
| 작업 | 상태, 중지, 신규 | 글 바로 가기(종료를 입력해도 여전히 돼요. 세션 종료는 /status → ⋮에 있어요) |
| 탐색 | 템플릿, 명령어 | Mini App은 입력창 옆 메뉴 버튼이나 /app으로 열어요. 이 키보드에서는 열 수 없어요 |
모델 ›는 명령어 하위 메뉴 안에 있어요. 하위 메뉴의 항목을 누르면 미리 써 둔 메시지나 슬래시 명령을 보내요:
- 템플릿: 계속 반복해서 입력하게 되는 메시지예요. 예: “테스트 작성”, “계획 설명”, “메모리 업데이트”.
- 명령어: Claude Code가 직접 처리하는 슬래시 명령(
/compact,/clear등)이고, 바로 입력돼요. 명령이나 템플릿은 어느 세션으로 갔는지 적어요(🧹 x1 세션으로 보냈어요: /clear). 세션이 작업 중이면 대화 지우기는 거부돼요(⚠️ 세션이 작업 중이라 대화를 지울 수 없어요). - 모델: 현재 세션의 모델을 빠르게 바꿔요. 계열 별칭(
sonnet,opus,haiku,fable,opusplan)은 항상 그 계열의 최신 모델을 가리키고, 고정 모델(claude-opus-5-5, 1M 컨텍스트의claude-opus-5-5[1m],claude-sonnet-5-5,claude-fable-5-1,claude-haiku-5-5)도 있어요. 시작할 때의 선택기와 Mini App의 세션 페이지도 같은 목록을 써요. 세션이 작업 중이거나 열린 요청이 있으면 변경은 거부돼요. 그 뒤🔄답은 세션이 알려 오는 모델로 바뀌거나, 변경이 확인되지 않았다고 알려 줘요(실행 중인 세션의 모델 바꾸기 참고).
기본 배치를 바꾸려면 ~/.config/aipager/keyboard.json을 쓰세요:
{
"templates": [{"label": "Deploy", "prompt": "Deploy to staging"}],
"commands": [{"label": "Compact", "send": "/compact"}],
"models": [{"label": "Sonnet", "send": "/model sonnet"}]
}
부분마다 따로예요. 빠진 부분은 기본값을 쓰니까, 다른 부분을 쓰지 않고 한 부분만 바꿀 수 있어요. 망가진 JSON은 무시되고 로그에 경고가 남아요. 바꾸면 aipager를 재시작해야 해요.
메시지마다의 버튼#
봇의 답은 대부분 상황에 맞는 버튼을 달고 와요:
권한 요청#
Claude가 승인이 필요한 도구를 실행하려고 하면 작업 카드가 권한 요청이 돼요:
🔐 Bash: List the temp directory
ls -la /tmp
[✅ 허용] [❌ 거부]
[🟢 항상 허용] [⏹ 중지]
카드는 Claude 자신의 설명 아래에 Claude가 실행하려는 진짜 명령(또는 파일 경로)을 보여 줘요. 읽고 이해한 것을 승인하세요. 글이 그려지는 순서를 바꿀 수 있는 문자(U+202E 같은 Unicode 방향 제어 문자로, 어떤 명령을 다른 명령처럼 보이게 할 수 있어요)는 ⟨U+202E⟩ 같은 표시로 나오고, 그러면 카드에 ⚠️ 글자 방향을 바꾸는 숨은 문자가 들어 있어요(⟨U+...⟩로 표시). 보이는 것과 다른 것이 실행될 수 있어요. 줄이 더해져요.
- 허용: 이번 호출을 승인해요.
- 거부: 거부해요. Claude는 그 도구를 실행하지 않아요.
- 항상 허용: 승인하고, Claude가 제안하는 오래 가는 규칙(“don't ask again for …”)을 더해요. 이 버튼은 Claude가 그런 규칙을 제안할 때만 나와요. 규칙을 만들 수 없는 명령(대부분의 복합 명령)에는 카드가 대신 허용 / 거부 / 중지를 보여 주고, 세션 작업 폴더 밖의 읽기 전용 파일 접근(
Read,Grep,Glob)도 그래요. Claude Code 2.1.259 이상은 그 자리에 Bash 대화 상자에서는 “switch to auto mode” 줄을, 바깥 읽기 대화 상자에서는 “block reads outside the working directories from now on” 줄을, 파일별 규칙을 만들 수 없는 Write/Edit에서는 모든 파일 편집을 자동으로 받아들이는 것(acceptEdits) 같은 권한 모드 전환을 둬요. aipager는 이런 줄을 절대 고르지 않아요. 모드는/mode로 직접 바꾸세요. - 중지: 답하는 대신 작업을 중단해요.
답은 aipager의 권한 hook을 거쳐 Claude Code로 가고, 이 hook은 최대 20초 기다려요. 작업 카드보다 먼저 도착한 요청(그때는 허용과 거부만 있는 별도 메시지로 보내요)도 같은 방식으로 답해요. 그 기다림이 이미 지났으면 버튼이 Claude Code 자체 대화 상자에 답을 입력해요. 그때 거부는 대화 상자의 마지막 줄(“No, and tell Claude what to do differently”)을 고르는데, 이 줄은 중지처럼 Claude의 작업을 끝내고, 카드에 🚫 jim · 거부됨이 나와요.
누를 때마다 ~/.claude/aipager-audit.jsonl에 기록되고, 작업 카드 아래에 한 줄 답으로 되풀이돼요: ✅ jim · 허용됨 · @alice · Bash: List the temp directory(호출의 설명, 설명이 없으면 명령이에요. aipager config로 설정한 채팅에서의 모양이고, 개인 모드에서는 허용됨만 나와요).
요청(또는 AskUserQuestion)이 나를 기다리는 동안 세션은 답변 대기 중으로 보이고, 절대 준비됨으로 보이지 않아요. 기다리는 동안 Claude Code가 입력이 조용하다고 알려 주면, 채팅은 그 요청에 대한 답장으로 ⬆️ jim · 위에서 아직 답변을 기다리고 있어요를 기다림마다 한 번 받아요.
AskUserQuestion 대화 상자도 같은 방식으로 나오고, 선택지마다 버튼이 하나씩(처음 네 개) 있으며, 여러 개를 고를 수 있는 질문에는 ⬜/☑ 체크 상자와 ✅ 제출 버튼이 있어요. 질문과 선택지마다의 설명은 터미널에서처럼 전부 보여 줘요. Telegram 메시지 하나에 담기에 너무 긴 양식만 줄여요. 작업 카드가 먼저 도구 줄을 덜 보여 주고, 그다음 질문과 설명 중 가장 긴 글을 단어 경계에서 …로 끝내요.
요청이 둘 이상일 때. Claude Code는 요청 두 개를 동시에 기다릴 수 있어요. 백그라운드 에이전트가 권한을 요청하는 동안 Claude가 질문을 하거나, 그 반대인 경우예요. aipager는 두 요청을 모두 보여 줘요. 첫 번째는 작업 카드에(또는 별도 메시지로), 그다음 요청은 각각 카드 아래 별도 메시지로, 저마다 버튼과 함께 보여 줘요. 마지막 요청에 답할 때까지 세션은 기다리는 상태로 보이고, 그사이에 보낸 메시지는 그때까지 보류돼요. 어떤 순서로 답해도 돼요. 허용과 거부는 권한 hook이 기다리는 동안 hook을 통해 가고, hook은 그 요청이 어디 있든 바로 그 요청에 닿아요. 키를 입력해야 하는 탭(질문의 선택지, 또는 hook의 기다림이 끝난 뒤의 허용과 거부)은 Claude Code가 먼저 보여 주는 요청에만 키를 입력해요. Claude Code는 대화 상자를 한 번에 하나만 보여 주고, 키는 맨 위의 대화 상자로 가기 때문이에요. 그렇지 않으면 탭은 아무것도 입력하지 않고 버튼도 그대로 두며 Claude Code가 다른 요청을 먼저 보여 주고 있어요. 그 요청에 먼저 답한 다음 이 요청에 답해 주세요.라고 알려 줘요. aipager가 순서를 모를 때는(예를 들어 시작을 보지 못한 에이전트라면) Claude Code가 어떤 요청을 먼저 보여 주는지 aipager가 알 수 없어요. 이 요청은 터미널에서 답해 주세요.라고 알려 줘요. 카드의 요청에 먼저 답하면 카드는 중지 버튼만 남긴 채 기다리는 중이라고 보여 주고, 다른 요청들은 버튼을 그대로 가져요. 에이전트의 요청이 아직 열려 있는데 Claude가 작업을 끝내면, 답은 평소처럼 오고 세션은 그 요청을 계속 기다려요. 그 요청은 별도 메시지로 다시 보내요. 요청이 두 개 이상 열린 채로 재시작하면 아무것도 되살아나지 않아요. 그 버튼은 이미 답했어요라고 답하고 아무것도 입력하지 않으며, 요청에는 터미널에서 여전히 답할 수 있어요. 열린 요청이 하나뿐이면 전처럼 되살아나요.
컨텍스트가 80%에 이르면 채팅은 ⚠️ name · 컨텍스트 82% 사용 - 곧 자동으로 압축해요를 📦 지금 압축과 함께 받아요. 이 버튼은 그 세션에 /compact를 보내요. 이 경고는 대화마다 한 번만 와요. 압축해도 다시 오지 않고, 새 대화(예를 들어 /clear 뒤)에서는 다시 와요.
오래된 버튼#
실행 중인 작업에 작용하는 버튼(중지, 종료, 재시작, /mode의 전환 버튼과 그 “중지 후 모드 전환” 버튼, /new의 “새 세션으로 교체”)은 그것을 보여 준 바로 그 작업에 묶여요. 이전 작업에서 남은 버튼은 지금 실행 중인 작업에 작용하는 대신 아무것도 바꾸지 않고, 세션이 끝났다가 재개되거나 재시작되기 전에 보여 준 버튼도 그래요. 종료, 재시작, /mode의 전환 버튼, 그리고 /status나 /stop 선택기의 중지는 x1 세션이 새 작업을 시작했어요…와 함께 다시 누르거나 다시 보낼 것을 알려 주고(또는 그 세션은 종료됐어요.), 작업 카드의 중지는 그 작업은 이미 끝났어요…라고 답하며, /mode의 “중지 후 모드 전환”은 카드를 ⚠️ x1 세션이 새 작업을 시작해서 아무것도 바꾸지 않았어요. 줄 아래의 /mode 카드로 되돌려요. 카드가 나온 뒤 세션이 새 작업을 시작했으면 /new의 “새 세션으로 교체”는 x1 세션이 그사이 작업을 시작했어요. 바꾸려면 /new를 다시 보내세요.라고 알려 줘요.
요청의 버튼은 그 요청에 묶여 있어요. Telegram에서든 터미널에서든 답하고 나면, 세션의 다른 요청이 아직 열려 있어도 그 버튼은 이미 답했어요라고 답하고 아무것도 입력하지 않아요.
⚡ 지금 보내기 버튼은 자기가 달린 메시지에만 작용해요. Claude가 이미 그 메시지를 가져갔으면 누름은 Claude가 이미 가져갔어요라고 답하고, 아무것도 보내지 않으며, 대기 줄을 지워요.
작업이 끝났을 때의 답변#
작업이 끝나면 답변은 내 메시지 배치에 따라 와요(아래와 설정 참고). Claude의 답변이 메시지 하나에 담길 수 있는 것보다 길면(약 32 KB), 메시지는 📎 전체 응답은 아래 파일에 있어요 ↓ 줄 아래에 들어가는 만큼 보여 주고, 작업 전체(단계와 전체 답변)는 나중에 <name>_full_log.md로 와요. Telegram은 다른 앱 없이 이 파일을 보여 줄 수 있어요. 작업 카드가 줄을 덜어 내야 할 때도 같은 파일이 붙어요.
Anthropic API 오류(크레딧 소진, 과부하, 요청 수 제한, 네트워크)로 끝난 작업은 답변 대신 ⚠️ name · <what went wrong>을 🔄 다시 시도와 함께 보여 줘요. 이 버튼은 가장 최근 메시지를 같은 세션으로 다시 보내요. 채팅의 보내기가 멈춘 동안에는 눌러도 아무것도 하지 않으니(메시지는 다시 보내지 않고 버튼은 남아요), 차단이 풀린 뒤 다시 누르세요.
카드가 있는 배치(“작업 카드 + 따로 오는 답변”)에서는 끝난 카드가 답변 위에 남아서, 답에 이르기까지의 기록이 돼요. 도구 줄, 에이전트 줄, 그 사이에 Claude가 한 말이에요. 답변은 별도 메시지로 와요(알림과 함께). 도구를 하나도 호출하지 않은 작업(도구, 에이전트, 백그라운드 명령, 질문이나 권한 요청이 하나도 없는 작업)은 ✅ name · 완료 · Ns만 남기게 되니까, 그 카드는 아직 작업 중이라고 나올 때 지워지고 완료로 바뀌지 않아요. 대신 답변의 첫 줄이 시간을 보여 줘요(💬 name · 완료 (…)). 파일을 보내는 건 여기서 도구 호출로 치지 않아요. 파일은 답변 뒤에 와요. 보여 줄 답변이 없는 작업이나 API 오류로 끝난 작업은 카드를 그대로 둬요. 작업마다 메시지를 하나만 원하면 “답변을 카드 안에”(답변이 카드로 들어가요)나 “카드 지우고 답변만”(카드가 지워지고 답변만 남아요)을 고르세요.
작업마다 작업 카드는 정확히 하나예요. 작업이 실행되는 동안 보낸 메시지가 그 작업에 두 번째 카드를 더하는 일은 절대 없고, 작업이 끝난 카드에는 더 이상 중지 버튼이 없어요. 아무것도 카드를 닫지 않으면(잃어버린 Stop hook, 재시작) aipager가 30초 뒤에 내 배치에 따라 닫아요. aipager가 재시작한 뒤에는 작업이 아직 실행 중인 카드는 남아서 바로 이어 가고, 시간은 작업이 시작된 때부터 이어서 세며, 작업이 끝날 때까지 세션은 작업 중으로 보여요. 재시작 전에 남은 다른 카드는 모두 닫혀요(“aipager 재시작됨”, 중지 버튼 제거). 권한 요청이나 Claude의 단일 질문을 기다리던 세션은 작업 중이 아니라 기다리는 상태로 돌아와요. 그 카드와 고정된 표시줄은 나를 기다린다고 알려 주고, 저장된 대화에 그사이 터미널에서 답했다고 나오지 않는 한 요청의 버튼으로 여전히 답할 수 있어요. 여러 부분이나 여러 선택지가 있는 질문, 그리고 서브에이전트가 낸 요청은 터미널에 맡겨요. 요청이 끝난 것이 확실해지면(누름이나 터미널로 답했거나, 그 작업이 끝났거나, 그사이 답해졌거나 저장해서 다시 읽을 수 없어서 재시작 뒤에 되살리지 않은 경우), 예전 답하기 버튼은 “이미 답했어요”라고 답하고 아무것도 입력하지 않아요. 재시작한 뒤에도요. 아무도 답하지 않은 요청은 Claude가 기다리는 동안 계속 열려 있어요. aipager는 Claude에게서 5분 동안 소식이 없고 저장된 대화에 그 요청이 이미 지나갔다고 나올 때만(답했다는 소식이 사라진 경우) 기다리기를 멈춰요. 그런 요청도 끝난 것으로 보지 않으니, 그 버튼은 여전히 대화 상자에 답해요. 이때 소식은 Claude 자신과 요청한 에이전트의 소식만 쳐요. 혼자 일하는 백그라운드 에이전트는 치지 않고, 저장된 대화는 에이전트의 요청에 대해 아무것도 알려 주지 않아요. 그런 요청은 그 에이전트 자신의 소식을 기다려요.
Claude가 스스로 시작한 작업(진행 중인 일이 없을 때 백그라운드 에이전트가 <task-notification>으로 결과를 보고하는 것)은 정말로 무언가를 할 때만 작업 카드를 받아요. 첫 도구 호출 때, 또는 15초 뒤, 먼저 오는 쪽이에요. 이런 스스로 깨어남은 대부분 몇 초짜리 “새로운 것 없음”이고, 이제는 답변만 보여 줘요. 실행되는 동안 “입력 중…” 표시는 여전히 나오고, 답변 자체는 항상 보내요. 중요하지 않다고 걸러지는 것은 없어요. 내가 시작한 작업은 Telegram이든 터미널이든 여전히 바로 카드를 받아요.
세션이 바쁜 동안 Claude가 시작하는 백그라운드 에이전트마다(Claude Code의 Agent 도구로, 예전 버전에서는 Task) 작업 카드에 자기 줄이 있어요: 🤖 <type> · <activity> · <elapsed>. 에이전트의 종류와 하고 있는 일을 보여 주고, 자기 도구 호출이 올 때마다 새로 고쳐져요. 그 에이전트가 도구를 세 번 이상 호출하면 그 호출들은 그 줄 바로 아래의 별도 ▸ 도구 N번 사용 버튼으로 접혀요. 메인 작업의 타임라인이나 그 Bash ×N 개수에는 절대 나오지 않아요. 에이전트가 끝나면 그 줄은 ✅ 🤖 <type> · 도구 N번 사용 · <elapsed>로 고정되고 그 버튼을 유지해요. 다른 에이전트가 시작한 에이전트(도우미)는, 시작되는 순간 aipager가 누가 시작했는지 알면, 자기 종류 앞에 그 사슬의 맨 위 에이전트 이름을 붙여요: 🤖 pipeline-runner › ship-tester. 줄은 에이전트가 시작한 순서대로 남아요. 위에서 말한 전체 .md 첨부 파일에는 에이전트 부분이 더해져서, 그 작업에서 실행된 모든 에이전트와 실행 시간, 도구 횟수, 호출한 도구를 나열해요.
백그라운드 에이전트가 자기 작업이 끝난 뒤에도 실행 중인 동안 카드는 그 일의 상태로 남아요. 그동안 Claude가 새 메시지를 받으면, 지금 카드는 그 메시지 아래의 카드이고 여전히 실행 중인 에이전트를 보여 줘요. 준비된 세션에 보낸 메시지는 새 작업을 시작해요. 이전 작업의 카드는 그 자리에서 고정되고 단계는 남으며, 새 작업은 자기 카드를 받아요. 대기열에 있다가 가져가진 메시지는 그 일의 작업을 이어 가요. 그 카드가 그 메시지 아래로 다시 보내지고 예전 것은 지워져요. 에이전트 안의 도구 호출은 메인 작업 자체의 줄로 절대 나오지 않고, aipager가 새 작업이 시작됐다고 생각하게 만들지도 않아요.
Claude의 백그라운드 명령(run_in_background, Ctrl+B, 또는 너무 오래 실행돼서 Claude Code가 백그라운드로 옮긴 명령)은 실행되는 동안 자기 줄이 있어요: ⏳ 백그라운드 명령: <description> (3:12). 호출의 설명으로, 없으면 명령의 첫 줄로 이름을 붙여요. 끝나면 그 줄은 ✅ 백그라운드 명령: <description> - 완료 (6:12), ❌ ... - 오류로 끝남 (종료 코드 1), 또는 ⏹ ... - 중지됨 (6:12)(Claude가 TaskStop으로 멈췄거나, Claude Code가 메모리 부족으로 멈춤)으로 고정되고, 실패와 중지 줄은 끝난 카드에 그 표시를 유지해요. 그런 명령이 아직 실행 중일 때 끝난 작업은 아직 끝난 게 아니에요. 카드가 그 일의 상태로 기다리고(🔄 name · 백그라운드 명령 1개 아직 작업 중 · 4:10, 에이전트도 있으면 에이전트 1개 (ship-reviewer), 백그라운드 명령 1개 아직 작업 중), 명령이 끝나면 Claude의 이어지는 부분이 같은 카드에서 계속돼요. 에이전트 안에서 시작한 명령은 그 에이전트의 것이고 보여 주지 않아요. aipager가 끝나는 것을 끝내 보지 못한 명령(재시작, 잃어버린 이벤트)은 두 시간 뒤에 세지 않아요(AIPAGER_BG_SHELL_MAX_TRACK, 초 단위). 그 줄에는 ⏹ ... - 끝난 것을 확인하지 못함이 적히고, 그것만 기다리던 일은 ⚠️ name · 완료 (2:00:04, 백그라운드 명령 소식 없음)으로 닫혀요.
작업의 타임라인이 길어지면, 오래된 도구 호출 묶음(연달아 세 번 이상이고, 지금 실행 중인 묶음이 아닌 것)마다 그 자리의 별도 ▸ 도구 N번 사용 버튼으로 접혀요. 모두 쌓이거나 Telegram의 메시지 길이 한도에 잘리는 대신이에요. 어느 버튼이든 누르면 그 자리에서 읽을 수 있어요. 설명 글은 절대 접히지 않고, 최신 활동과 상태 줄은 누르지 않아도 항상 보여요. 아직 실행 중인(또는 막 고정된) 백그라운드 에이전트 자기 줄은 버튼으로 접히지 않고 도구 호출만 접히며, 그것이 타임라인과 한도 사이의 유일한 것일 때도 접히지 않아요. 모두 접어도 들어가지 않을 만큼 타임라인이 커질 때만 카드에서 정말로 내용을 덜어 내고, 그때는 위의 .md 첨부 파일에 전체 기록이 있어요.
답변이 나갈 때 아직 실행 중인 에이전트#
Claude Code는 에이전트를 백그라운드 모드로 실행할 수 있고, 그것들을 시작한 작업은 에이전트가 아직 일하는 동안 끝날 수 있어요. 그러면 답변은 구분선과 그 백그라운드 일의 짧은 상태로 끝나요. Claude Code 자체의 에이전트 패널 같은 모양이고, 실행되는 동안 계속 업데이트돼요. 구분선 아래는 모두 작은 글씨(Telegram의 바닥글)라서 답변 자체가 눈에 띄어요:
⏳ 에이전트 5개 작업 중 · 🖥 백그라운드 명령(shell) 1개 실행 중
○ 🤖 pipeline-runner · Writing final.md for the pipeline
- 🤖 ship-tester · Write and run criteria tests
- 🤖 ship-developer · Editing state.py
- 🤖 ship-developer › ship-reviewer · Collect line numbers
‖ 🤖 r2-runner · 자기 에이전트를 기다리는 중
에이전트 작업이 끝나면 결과가 바로 여기로 와요.
- 첫 줄은 개수예요. 작업 중인(또는 끝난) 에이전트, 그리고 이 일의 실행 중이거나, 끝났거나, 중지된 백그라운드 명령(shell)이에요. 백그라운드 명령은 항상 숫자일 뿐이고, 명령 자체는 절대 보여 주지 않아요.
- 에이전트마다 한 줄, 🤖와 굵은 종류, 그다음 하고 있는 일이에요. 자기 최신 글 줄(Claude의 말, 쓴 그대로 보이고 절대 번역하지 않아요), 없으면 무엇을 하고 있는지 알려 주는 짧은 문구로 된 최근 도구(
Reading state.py,Writing final.md,Searching for …, 또는 명령 자체의 설명), 그것도 없으면 Claude가 에이전트를 시작할 때 준 작업, 그것도 없으면 최근 도구만(Running a command,Using TodoWrite), 그것도 없으면 종류만이에요. 그래서 같은 종류의 에이전트 둘이 설명 없는 명령을 실행해도 각자 자기 작업이 보여요. 자기가 시작한 에이전트를 기다리려고 멈춘 에이전트는 대신‖ … 자기 에이전트를 기다리는 중이라고 나와요. - 트리로 보이는 에이전트. 세션이 시작한 에이전트가 첫 단계예요. 그중 하나가 시작한 에이전트는 깊이와 상관없이 모두 바로 그 아래에, 각자 자기를 시작한 에이전트 바로 다음에 나와요. 둘째 단계의 에이전트가 시작한 에이전트는 그 에이전트 이름을 먼저 붙여요(
ship-developer › ship-reviewer). 작업 카드는 대신 같은 에이전트를 맨 위 에이전트 다음에 붙여 불러요(pipeline-runner › ship-reviewer). 단계는 절대 둘을 넘지 않아요. 둘째 단계에서 일하는 에이전트에는 자기 ○가 없어요. 거기에는 Telegram이 글머리 기호를 그려요. aipager가 아직 누가 시작했는지 모르는 에이전트는 추측 아래가 아니라 첫 단계에 보이고, 시작한 에이전트를 알게 되면 그 아래로 옮겨 가요. - 최대 여섯 줄, 아직 일하는 에이전트가 먼저 나오고, 줄이 없는 에이전트는
+에이전트 N개 더로 나와요. 에이전트는 자기 위의 에이전트 없이 혼자 나오지 않아요. - 약속 “에이전트 작업이 끝나면 결과가 바로 여기로 와요.”는 아직 작업 중인 에이전트가 있는 동안만 남아요. 백그라운드 명령에는 이 약속이 절대 없어요. 개발 서버는 영원히 끝나지 않을 수 있으니까요.
에이전트마다의 줄은 그 에이전트가 멈추면 스스로 고정돼서 ✓ 🤖 Explore · 완료 (0:42)(그 에이전트가 실행된 시간)가 되고, 첫 줄의 숫자가 줄어들어요. 목록에 있는 에이전트가 답변이 나간 뒤에 시작한 에이전트는 다음 업데이트 때 그 아래의 상태에 들어가요. 세션 자체가 나중에 시작한 에이전트는 대신 나중 답변 아래에 나와요. 모든 에이전트가 끝나면 이 줄들은 답변부터 마지막 에이전트가 끝날 때까지의 시간이 붙은 한 줄로 바뀌어요:
✅ 에이전트 2개 완료 (6:12) · 🖥 백그라운드 명령(shell) 1개 실행 중
그리고 그 백그라운드 명령이 끝나면 ✅ 에이전트 2개 완료 (6:12) · 백그라운드 명령(shell) 1개 종료 (2:13:05)가 돼요(명령이 실행된 시간. 종료됐거나 Claude가 멈췄으면 중지됨, 오류가 있든 없든 스스로 끝났으면 종료예요. 카드의 그 줄이 어느 쪽인지 알려 줘요). 일에 백그라운드 명령 하나만 있는 답변은 🖥 백그라운드 명령(shell) 1개 실행 중을 보여 주고, 백그라운드 명령(shell) 1개 종료 (…)나 백그라운드 명령(shell) 1개 중지됨 (…)으로 고정돼요.
답변 자신의 일에 속한 백그라운드 명령만 세어요. 이전 작업이 실행해 둔 개발 서버가 모든 답변 아래에 되풀이되지 않아요(고정된 표시줄은 여전히 모든 백그라운드 명령을 세어요). /stop은 그 일을 끝내지만, Escape는 백그라운드 명령을 끝내지 않아요. 아직 실행 중인 명령은 끝날 때까지 고정된 표시줄에 남고, 이후 답변 아래에서는 더 이상 세지 않아요. 나중에 이어지는 부분은 별도의 작업을 시작해요.
이 답변은 작업이 끝나는 대로 내 메시지에 답장하는 보통 메시지로(알림과 함께) 보내요. 터미널이 보여 주는 바로 그 글이에요. 작업 카드는 모든 배치에서 그 위에 그 일의 실시간 상태로 남아요: 🔄 name · 에이전트 1개 (general-purpose) 아직 작업 중 · 1:18, 그리고 중지 버튼과 함께예요. 에이전트가 결과를 보고하면 그 부분에 대한 Claude의 답변이 별도의 새 메시지로 와요. 이전 답변은 aipager가 재시작해도 절대 다시 보내지 않아요. 에이전트가 여러 번 보고하는 일은 보고마다 답변이 하나씩 오고, 답변마다 다 쓰이는 대로 보내요.
자기 백그라운드 일이 아직 실행 중인데 멈춘 에이전트는 나중에 이어서 하니까 작업 중으로 쳐요. 자기 에이전트를 시작한 뒤 그것을 기다리려고 멈춘 에이전트는 그 시작(다른 에이전트를 시작한 자기 호출)으로 알아봐요. 이 에이전트는 자기 에이전트가 하나도 실행 중이지 않은 상태로 멈출 때까지 상태에 작업 중으로 남고, 그 일도 열어 둬요. 그중 마지막 에이전트가 끝나고 2분이 지나도 이어서 하지 않으면 더 이상 실행 중으로 세지 않고, 상태는 마지막으로 보여 준 그대로 둬요. 에이전트에 대한 Claude의 <task-notification>도 메모로 같은 내용을 알려 줘요. aipager는 그 알림이 작업을 시작할 때, 그리고 작업 카드가 떠 있는 동안 Claude가 실행 중인 작업 한가운데에서 그 알림을 받을 때도 이 메모를 읽어요. 줄은 완료로 보여 주기 전에 몇 초 기다려서, 그 메모가 먼저 다시 가져갈 시간을 줘요. 상태는 모든 배치에서, 채팅의 언어로 붙어요.
어떻게 업데이트되나요. aipager는 상태가 보여 주는 것이 바뀌었을 때만, 답변마다 최대 30초에 한 번(답변 자체가 첫 번째로 세어져요) 조용히 답변을 편집해요. 상태가 있는 모든 답변이 이렇게 업데이트돼요(세션마다 기다리는 답변 최대 다섯 개). 편집은 우선순위가 낮아요. 보내기 예산에 거부되거나 채팅의 보내기가 멈춘 때에 걸린 편집은 하지 않고, 상태는 다음 변화를 기다려요. 마지막 편집, 즉 모든 것이 끝났을 때의 편집은 보내기 멈춤이 풀린 뒤나 예산이 거부했을 때 한 번 더 시도하고, 그다음 포기해요. 지워진 답변, 끝났거나 종료된 세션의 답변은 편집하지 않고, 답변이 Telegram에 너무 길어지는 편집도 하지 않아요(긴 답변을 자를 때 상태를 위한 자리를 남겨 둬요).
aipager가 일이 끝났는지 알 수 없을 때 상태는 마지막으로 보여 준 그대로 두고, 절대 완료로 바꾸지 않아요:
- aipager가 30분 동안 소식을 듣지 못한 에이전트(
AIPAGER_SUBAGENT_SILENCE)는 더 이상 실행 중으로 세지 않지만, 조용하다고 끝난 것은 아니에요. - 끝나는 것을 보지 못한 채 두 시간(
AIPAGER_BG_SHELL_MAX_TRACK)보다 오래 실행된 백그라운드 명령은 더 이상 세지 않아요. - aipager가 재시작하면 어떤 답변이 기다리고 있었는지 잊어요.
aipager가 재시작해도 아직 실행 중인 에이전트는, 자기 에이전트를 기다리는 에이전트까지 포함해 그대로 남아요. 재시작 뒤에 보내는 답변에 그 에이전트들이 나오고, 재시작이 이어받은 작업 카드에 다시 보이며, 나중에 끝나는 것도 평소처럼 알아봐요. 재시작보다 30분 넘게 전에 저장된 에이전트는 되살리지 않아요.
보내기 멈춤 때문에 보류된 답변, 또는 서식 있는 보내기가 실패한 뒤 일반 텍스트로 보낸 답변은 상태 없이 보내고, 답변 글 없이(첫 줄만) 끝난 작업도 마찬가지예요.
종료 확인#
/kill, 그 선택기, ⋮ → ⏹ 세션 종료는 항상 확인을 요청해요:
⏹ jim 세션을 종료할까요? Claude가 작업을 멈추고 세션이 닫혀요.
[⏹ 종료] [취소]
⏹ 종료 뒤에는 메시지가 이 채팅의 남은 세션 위에 ⏹ jim 종료됨을 보여 줘요. 세션이 새 작업을 시작한 뒤에 누른 확인은 거부되고(jim 세션이 새 작업을 시작했어요…), 나중에 시작한 같은 이름의 세션에 대한 확인도 그래요.
음성 메시지 기능 설치 (추가 패키지가 없을 때)#
aipager[voice]가 설치되지 않았는데 음성 메시지를 보내면:
⚠️ 음성 메시지를 쓰려면 선택 패키지가 필요해요
(설치 약 200MB · 처음 쓸 때 모델 약 74MB).
[📦 음성 메시지 기능 설치] [취소]
설치를 누르면 내 설치 프로그램에 맞는 설치 명령(uv tool install --reinstall aipager[voice], pipx install --force, 또는 대체 수단인 pip install faster-whisper)을 실행하고, 5초마다 메시지를 고쳐서 아직 진행 중이라고 알려 주고, 성공하면 [🔄 aipager 재시작] 버튼을 제안해요.
재시작#
🔄 aipager 재시작:
- systemd 사용자 서비스로 실행되는 aipager는 5초 뒤의 분리된
systemctl --user restart aipager.service를(1초까지 정확한 타이머로), aipager 자체 프로세스 그룹 밖의 임시 서비스 안에 예약해서 aipager가 끝나도 살아남게 해요. 서비스 파일이 내 세션을 종료하게 되어 있으면(KillMode가process나none이 아님) 거부하고, 먼저aipager service install을 실행하라고 해요. - macOS에서 launchd 에이전트가 설치되어 있으면(
~/Library/LaunchAgents/com.aipager.daemon.plist):launchctl kickstart -k gui/<uid>/com.aipager.daemon. 내가 직접 시작한 aipager도 마찬가지예요. - 그 밖에 내가 직접 시작한 aipager(
aipager start)는, Linux에서 서비스도 설치되어 있더라도, 분리된 대체 프로세스를 만들어요. 이 프로세스는 부모가 끝나기를 기다렸다가 aipager를 시작했던 것과 같은 명령을 실행해요(출력은 자체 재시작 로그로 가요). 지금의 aipager는 대체 프로세스가 살아나면 바로 깔끔하게 멈춰요.
SSH는 필요 없어요. aipager 업데이트를 마치려면 업데이트 카드의 🔄 지금 재시작이 대신 같은 터미널에서 aipager를 제자리에서 재시작해요(업데이트 참고).
업데이트#
/update(관리자 전용. 개인 모드에서는 봇 소유자만)는 버튼 하나로 답해요: 🔄 업데이트 확인. 누르기 전에는 아무것도 조회하지 않아요. 누르면 그 메시지를 제품마다 한 줄로 바꿔요:
- 더 새 버전이 있으면(PyPI의 최신 버전)
aipager 1.0.1 → 1.1.0; - 없으면
aipager 1.0.1 (최신); - 조회에 실패하면(네트워크 문제, 5초 시간 초과, 또는
claude를 찾지 못함)Claude Code 2.1.282 (확인 못 함).
Claude Code는 자기 업데이트 채널(autoUpdatesChannel: latest, stable, rc)의 최신 버전과 비교해요. 그 줄들 아래에는 작은 글씨로 aipager의 설치 방식이 나와요(예: pipx, from PyPI 또는 pipx, from local path …. 그룹 채팅에는 경로를 절대 보여 주지 않아요).
더 새 것이 있으면 업데이트가 있는 제품만 업데이트하는 버튼이 하나 나와요. aipager 업데이트, Claude Code 업데이트, 또는 둘 다 업데이트, 그리고 취소예요. aipager 업데이트를 제안할 때는 재시작이 어떻게 될지도 알려 줘요: 재시작: 자동, 진행 중인 작업이 없을 때., 재시작: 설치되면 “지금 재시작”을 누르세요.(서비스 없이 실행되는 aipager, 아래 참고) 또는 재시작: 수동 (이유).
더 새 aipager가 이미 설치됐는데 실행 중인 aipager는 아직 예전 것이라면(재시작 없이 끝난 업데이트, 또는 직접 실행한 pip install -U), 그 줄은 aipager 1.1.0 버전이 설치됐고 재시작을 기다리고 있어요 (실행 중: 1.0.1).라고 나오고, 메시지는 업데이트 대신 🔄 지금 재시작을 제안해요. 재시작만 필요한 버전에는 aipager 업데이트를 절대 제안하지 않아요. 설치된 버전은 aipager가 재시작해 들어갈 인터프리터를 실행해서 확인하니까, 설치됐지만 불러올 수 없는 버전은 그렇다고 알려 주고(aipager 1.1.0 버전이 설치됐지만 불러오지 못해요. … 명령으로 다시 설치하세요.) 아무것도 제안하지 않아요. 설치된 것보다 더 새 릴리스는 여전히 업데이트로 제안해요. 여기서 업데이트할 수 없는 설치(editable, Nix, Snap, 시스템 패키지, 컨테이너, 또는 다른 사용자의 설치)의 더 새 aipager는 이유와 함께 보여 주지만 제안하지는 않아요. 내 사용자가 쓸 수 없는 폴더에 npm으로 설치한 더 새 Claude Code도 마찬가지예요(예: /usr/lib/node_modules의 sudo npm install -g). 거기서는 claude update가 실패할 수밖에 없어서, 그 줄에는 여기서 업데이트할 수 없음: installed with npm into a folder your user can't write to; run "claude install" once to switch to Claude Code's own installer, then restart aipager가 적혀요. 업데이트할 것이 없으면 메시지는 “모두 최신 상태예요.”라고 하거나, 확인 하나가 실패했다고 알려 주거나, 더 새 버전이 있지만 여기서 설치할 수 없을 때는 “업데이트가 나왔지만 여기서는 설치할 수 없어요.”라고 하고, 다시 확인 버튼만 있고 업데이트 버튼은 없어요.
업데이트 버튼은 확인이 제안한 것만 실행해요. 확인한 지 10분이 넘었거나, 그 뒤 다른 업데이트가 실행됐거나, 버전이 버튼과 더 이상 맞지 않으면 다시 확인하라고 해요. 예전 /update 메뉴의 버튼(제품마다 따로 있던 Claude Code 업데이트 / aipager 업데이트 / 둘 다 버튼)은 “이 메뉴는 오래됐어요. /update를 다시 보내세요”라고 답하고 아무것도 하지 않아요. 누를 때마다 관리자 전용 규칙을 다시 확인해요.
Claude Code 업데이트는 claude update를 실행하고(절대 경로로, 최대 5분 기다려요) Claude Code A → B, “이미 최신이에요”, 또는 오류와 출력의 끝부분을 알려 줘요. 실행 중인 세션은 내가 재시작할 때까지(/restart) 예전 버전을 써요. 답변이 그 세션들을 나열해요. 나 대신 저절로 재시작되는 세션은 없어요. 새 세션은 새 버전을 써요.
aipager 업데이트(aipager 업데이트, 또는 둘 다 업데이트의 뒷부분. Claude Code를 먼저 업데이트해요):
- 이미 최신인 PyPI 설치라면 그렇다고 알려 주고 멈춰요.
- aipager가 스스로 재시작할 수 있으면 먼저 반쯤 진행 중인 것이 없을 때까지 기다려요. 작업을 실행 중이거나, 질문을 기다리거나, 백그라운드 에이전트를 실행 중이거나, 살아 있는 작업 카드를 보여 주거나, 도구를 실행 중이거나, 열린 권한 요청이 있는 세션이 없고, 속도 제한이 풀리기를 기다리며 보류된 답변도 없어야 해요. 메시지는 무엇을 기다리고 있는지 나열하고 지금 재시작(기다림 건너뛰기)과 취소를 붙여요. 10분 뒤에 다시 물어요: 10분 더 기다리기, 지금 재시작, 취소. 한 시간 동안 아무도 답하지 않으면 스스로 취소해요.
- 실행 중인 aipager를 설치한 설치 프로그램으로 업그레이드해요(
pipx upgrade aipager,uv tool upgrade aipager,brew upgrade aipager, 또는<venv>/bin/python -m pip install --upgrade aipager). 절대 경로로, 최대 10분 기다려요. 설치 프로그램이 실행되는 동안에는 취소 버튼이 없어요. - 새 인터프리터에서 새 버전을 import할 수 있는지 확인해요. 실패했거나, 시간을 넘겼거나, import할 수 없는 업그레이드는 아무것도 재시작하지 않아요. 시간을 넘긴 업그레이드는 중간에 멈춘 것이라, 메시지가 설치가 불완전할 수 있다고 경고하고 다음 재시작 전에 실행할 다시 설치 명령을 알려 줘요. 설치 프로그램의 출력은 개인 채팅에서만 보여 주고, 그룹은 “(출력은 aipager 로그에 있어요)”를 받아요.
- 업그레이드하는 동안 작업이 시작되면 다시 기다려요.
- 5초 뒤의 분리된
systemctl --user restart aipager.service를(1초까지 정확한 타이머로) 예약하고, 상태 메시지에⏳ aipager A → B 설치됨, 재시작하는 중…이 나와요. 5단계에서 기다리는 동안에는⏳ aipager A → B 설치됨, 지금 작업이 끝나면 재시작해요가 나와요. 재시작한 뒤에는 새 aipager가 같은 메시지를✅ aipager 업데이트됨 A → B, 세션 N개 다시 연결됨으로 바꿔요(돌아오지 않은 세션이 있으면⚠️ 다시 연결되지 않음: …도). 그 메시지가 지워졌거나 더 편집할 수 없으면 결과를 요청한 채팅에 새 메시지로 올려요.
Mini App에서는 설정 → 업데이트가 새 aipager가 응답할 때까지 돌아가는 등불과 함께 “aipager 재시작 중…”을 보여 주고, 그다음 “B 버전으로 업데이트했어요. 세션 N개를 다시 연결했어요.”를 보여 줘요. 카운트다운은 절대 보여 주지 않아요. 약 2분 동안 응답이 없으면(예전 aipager가 재시작이 일어나지 않았다고 알려 줄 시간보다 일찍은 절대 아니에요) “아직 재시작하는 중이에요. 잠시 후 앱을 다시 여세요.”가 나와요. aipager의 관리형 터널에서는 업데이트 후 재시작이 앱 주소를 그대로 유지해서(새 aipager가 터널을 이어받아요), 열려 있던 페이지가 그대로 버티다가 “B 버전으로 업데이트했어요”를 보여 줘요. 그래도 터널을 유지하지 못했다면 약 2분 뒤 페이지에 “아직 재시작하는 중이에요. 잠시 후 앱을 다시 여세요.”가 나와요. 이 기능이 없는 aipager에서(또는 MINIAPP_KEEP_TUNNEL=0으로) 시작한 업데이트는 여전히 주소가 바뀌어요. 예전 aipager가 응답을 멈추면 열려 있던 페이지에 “aipager가 재시작했어요. 채팅에 업데이트됐다고 나오면 앱을 다시 여세요”가 나와요. 닫고 채팅의 메뉴 버튼이나 /app으로 다시 여세요. 앱의 로그인은 5분 동안 유지되니까, 그보다 오래 열어 둔 페이지에는 대신 “업데이트 결과를 보려면 앱을 다시 여세요.”가 나와요.
업데이트 뒤 aipager는 systemd 사용자 서비스로 실행되고 그리고 그 서비스 파일에 KillMode=process(또는 none)가 있을 때만 스스로 재시작해요. 안전하지 않은 KillMode라면 aipager는 여전히 업그레이드되고, 메시지에 재시작 필요: …가 나와요. 먼저 aipager service install을 실행하세요(재시작하면 종료될 세션을 나열해요).
서비스 없이 지금 재시작. 서비스 관리자가 재시작해 주지 않는 aipager(터미널이나 tmux의 aipager start, Docker, 또는 macOS의 launchd 에이전트)는 업데이트를 설치한 뒤 🔄 지금 재시작 버튼과 함께 “지금 재시작”을 누르면 실행돼요 (진행 중인 작업이 없을 때까지 aipager가 기다려요).를 보여 줘요. 누르면(다른 업데이트 버튼처럼 관리자만):
- 설치된 aipager를 불러올 수 있는지 확인해요(
⏳ 설치된 aipager를 확인하는 중…). 깨진 설치는 아무것도 재시작하지 않고, 같은 버전이면 재시작할 것이 없다고 알려 줘요. - systemd 재시작처럼, 진행 중인 것이 없을 때까지 지금 재시작(기다리지 않기)과 취소 버튼과 함께 기다려요.
- 설치를 다시 확인한 다음
⏳ aipager A → B 설치됨, 재시작하는 중…이라고 알리고 aipager를 깔끔하게 멈춰요(Ctrl+C처럼). - 새 aipager를 제자리에서 시작해요. 같은 프로세스, 같은 명령, 같은 환경과 폴더로, 같은 터미널이나 tmux 창에서(그리고 Docker에서는 같은 컨테이너 프로세스로) 실행돼요. 세션은 계속 실행돼요. 새 aipager가 메시지를
✅ aipager 업데이트됨 A → B, 세션 N개 다시 연결됨으로 고쳐요.
재시작하는 몇 초 동안 보낸 탭은 다른 재시작 때처럼 사라져요. 재시작 중에 Ctrl+C나 docker stop을 하면 그쪽이 이겨요. aipager가 멈추고 다시 시작하지 않아요. 새 aipager를 제자리에서 시작할 수 없으면 예전 것이 끝난 뒤 분리된 복사본이 시작해요(출력은 자체 재시작 로그, 즉 인스턴스 폴더의 aipager.log나 /tmp/aipager.log로 가고, 그 복사본이 돌아오지 않으면 실행할 명령을 aipager 로그가 알려 줘요). 그것마저 실패하거나 인터프리터 자체가 사라졌다면 aipager는 코드 1로 종료하고, aipager 로그에 실행할 명령과 함께 restart.failed:가 적혀요.
aipager가 스스로 재시작할 수 없을 때는 메시지가 버튼 대신 내 환경에 맞는 정확한 단계를 알려 줘요:
- 터미널이나 tmux:
aipager가 실행 중인 터미널이나 tmux 창에서 Ctrl+C를 누른 다음 aipager start 명령을 실행하세요. - macOS:
Mac에서 launchctl kickstart -k gui/<uid>/com.aipager.daemon 명령을 실행하세요. - Docker:
Docker가 실행되는 컴퓨터에서 docker restart <container> 명령을 실행하세요.(호스트 이름이 컨테이너의 짧은 ID일 때는 그 ID), 그리고 이미지에서 컨테이너를 다시 만들면 그 이미지에 든 aipager 버전으로 돌아간다는 안내도 함께 나와요.
지금 재시작은 더 새 aipager가 설치되어 기다리고 있을 때 업데이트 확인 결과에도 나오고, 한 번만 오는 알림에도 나와요. Claude Code의 hook(이벤트마다 설치된 aipager로 새로 시작해요)이 이미 실행 중인 aipager보다 더 새 aipager를 실행하고 있으면, 소유자는 더 새 버전과 aipager 실행마다 조용한 메시지 하나를 버튼과 함께 받아요: ⚠️ aipager 업데이트가 절반만 끝났어요. …. 이 메시지는 hook이 더 새 버전을 처음 실행한 때부터 2분을 기다리고(설치 바로 뒤의 재시작에서는 절대 나오지 않아요), 그다음 소유자의 개인 채팅에서 진행 중인 작업이 없을 때까지 기다려요. 그 버전으로 재시작한 뒤에는 알림이 버튼 없이 ✅ aipager 업데이트가 끝났어요. 이제 aipager는 B 버전으로 실행 중이에요.로 바뀌어요. Mini App에서는 설정 → 업데이트가 같은 상태를 카드 위의 한 줄로 지금 재시작과 함께 보여 줘요(먼저 물어봐요).
Mini App에서 업데이트는 설정 탭의 첫 번째 카드예요. 이름, 실행 중인 버전, 그리고 줄 끝의 작은 업데이트 확인이 있어요. 설정 탭을 열면 스스로 확인하고(최대 10분에 한 번, 업데이트가 실행 중이거나 aipager가 재시작하는 동안에는 절대 하지 않고, 거기서 실패한 확인은 아무 말도 하지 않아요), 탭하면 언제나 확인해요. 카드는 할 일이 생길 때까지 조용한 한 줄로 남아요. 더 새 버전이 있으면 그 줄들과 업데이트 버튼 하나가 나오고, “모두 최신 상태예요.”는 버튼 없는 한 줄이고, 실행 중인 업데이트, 재시작, 지금 재시작은 자기 버튼을 유지해요.
한 번에 업데이트는 하나만 실행되고, /update, Mini App의 설정 → 업데이트 카드(같은 확인, 같은 업데이트 버튼 하나, 같은 작업), 명령줄의 aipager update를 합쳐서 세어요. “설치됨, 재시작하는 중…”과 실제 재시작 사이의 몇 초도 포함이에요. /update는 aipager가 곧 재시작한다고 답하고, Mini App은 버튼을 제안하지 않고, 음성 메시지 기능의 aipager 재시작 버튼은 업데이트가 실행 중이거나 재시작을 기다리는 동안 거부해요. 재시작을 예약하고 2분이 지나도 aipager가 살아 있으면 기다리던 재시작 타이머를 멈추고, 업데이트 잠금을 풀고, 직접 재시작하라고 알려 줘요.
설치 프로그램이 실행되는 동안 aipager가 종료되면 설치 프로그램은 끝낼 시간 3초를 받은 뒤 멈춰요(그렇지 않으면 다음 aipager가 시작하는 동안 설치에 계속 쓸 거예요). 상태 메시지와, 재시작한 뒤의 새 메시지가 설치가 불완전할 수 있다는 것과 고치는 방법을 알려 줘요.
종료가 시작되면 아무것도 재시작하지 않고 새로 시작하지도 않아요. /update의 버튼은 aipager가 종료되는 중이라고 답하고, Mini App은 503으로 답하며, 설치 프로그램이나 버전 확인을 시작하지 않아요. 3초 안에 끝난 설치 프로그램은 여전히 인정돼요. 상태 메시지는 새 버전이 설치됐고 아무것도 재시작하지 않았다고 알려 주고, 다음 시작이 aipager 업데이트됨 A → B를 알려요. 내가 aipager service stop으로 멈춘 aipager는 멈춘 채로 있어요. 종료 중 업데이트 부분은 모두 합쳐 최대 8초 걸려요.
자유 메시지#
글#
현재 세션에 보내는 다음 메시지로 다뤄요. 이 채팅의 대상, 즉 가장 최근에 전환했거나, 메시지를 보냈거나, 시작했거나, 방금 여기서 나에게 답한 세션이에요(고정된 상태 표시줄 참고). 그룹에서는 사람마다 자기 대상이 있고 세션의 답변 때문에 바뀌지 않아요(그룹 참고). 메시지는 터미널에서 입력할 때처럼, 내 작업이 실행 중이어도 바로 Claude에게 가요(그룹에서는 다른 사람의 메시지가 그 작업이 끝나기를 기다려요. 아래 참고). 여러 개를 보내면 Claude 안에서 대기열에 들어가고, Claude는 자연스럽게 멈추는 지점에서 하나씩 가져가요:
내 메시지의 반응은 메시지를 따라 바뀌어요. Claude Code에서 대기 중인 메시지가 회색에서 흰색으로 바뀌는 것처럼요:
- 👀: 세션에 전달됐지만(또는 보류 중, 아래 참고) Claude가 아직 가져가지 않았어요. 작업이 실행되는 동안 보낸 메시지는 Claude의 대기열에서 기다리는 동안 계속 👀예요.
- 👍: Claude가 가져갔어요. 작업을 시작했거나, 실행 중인 작업에 합쳤거나, 실행 중인 백그라운드 에이전트에 넘겼어요.
- 🤷: 절대 가져가지 않을 거예요. 보류된 메시지가
/stop,/clearqueue,/kill로 버려졌거나 풀려날 때 보내지 못했어요. 아직 기다리는 메시지나 명령이/stop,/clearqueue,/kill, 또는 세션 종료로 버려졌어요(/clear나/resume은 아니에요). 또는 Claude Code가 거부한 메시지예요(아래 참고). Claude가 이미 대기열에 넣은 메시지는 aipager가 그 대기열을 아직 볼 수 있을 때만 표시해요(저장된 대화의 실시간 검사가 실행 중이고 기다리는 백그라운드 일이 없을 때). 아니면 👀로 남아요. 터미널의 Escape는 Claude의 대기열을 입력란으로 되돌리고, 거기서 다시 보낼 수 있으니까 그렇게 버려진 메시지도 👀로 남아요. - 👌: Claude Code 명령이 실행됐거나(아래 참고), aipager가
/stop을 확인했어요.
반응은 앞으로만 가니까 메시지는 최대 세 개의 반응을, 종류마다 한 번씩 받아요. Telegram이 속도 제한으로 막는 순간에 걸린 반응은 건너뛰고 나중에 보내지 않으며, 한 번의 정리는 버린 메시지 가운데 최신 열 개까지만 표시해요.
명령 버튼(대화 압축, /model …): 세션이 준비됐을 때 누르면 명령이 바로 👌를 받아요. Claude Code는 Enter를 누를 때 실행하고, /model 같은 로컬 명령은 나중에 알려 줄 hook을 보내지 않으니까요. 작업이 실행 중일 때 누르면(/model은 예외. 작업이 끝날 때까지 거부돼요. 실행 중인 세션의 모델 바꾸기 참고) 그 작업이 끝날 때까지(정상적으로, 백그라운드 일의 쉬는 지점으로, 또는 API 오류로) 👀였다가 👌가 돼요. 그전에 /stop, /clearqueue, /kill, 세션 종료로 버려지면 절대 실행되지 않아요: 🤷. Claude Code가 그것을 메시지로 대기열에 넣으면 👀 → 👍 길을 따라가요. 음성 메시지는 받아쓴 글이 나가고 나면 글과 같은 반응을 받아요.
작업 카드와 마지막 답변은 Claude가 작업에 실제로 쓴 메시지(세션이 준비됐을 때 작업을 시작한 메시지, 또는 실행 중인 작업에 합친 메시지)를 따라가고, 단순히 내가 마지막에 보낸 메시지를 따라가지 않아요. 중간에 메시지를 하나 더 보내도 답변이 그 메시지로 “뛰지” 않아요. Claude가 그것을 만들어지는 중인 답변에 합치면 카드가 그 순간 그 메시지 아래로 옮겨 가요. Claude가 먼저 끝내고 나서 가져가면 첫 답변은 첫 메시지 아래에 남고, 덧붙인 메시지는 자기 작업과 그 아래의 자기 카드와 답변을 받아요.
Claude Code가 메시지를 바로 거부하면(모르는 슬래시 명령, 또는 터미널에 대화 상자만 여는 기본 명령) hook이 오지 않으니, aipager가 방금 알린 작업을 끝내 줄 것이 없어요. 8초 동안 hook이 없으면(PROMPT_HOOK_GRACE_SECONDS) 작업 카드가 ⚠️ name · Claude Code가 메시지를 받지 않았어요와 설명 한 줄로 바뀌고 세션은 다시 준비돼요. 이유는 터미널에 있고, 메시지는 🤷를 받아요(슬래시 명령은 👌를 받아요. 대화 상자를 여는 기본 명령도 hook을 보내지 않으니까요). 새 작업을 시작한 메시지만 이렇게 판단해요. 실행 중인 작업 뒤에 줄 선 메시지는 Claude가 가져가거나 버려질 때까지 👀로 남아요.
세 가지 경우에는 보내지 않고 보류했다가, 끝나면 저절로 보내요:
- 열린 권한 요청이나 질문이 있어요. 열려 있는 동안은 계속 그래요(시간 제한은 없어요). 그렇지 않으면 내 글이 그 대화 상자에 대한 답으로 읽힐 거예요. 그때 보낸 첫 메시지에는 요청 아래에 Claude가 아직 답을 기다리고 있다는 답장이 하나 달려요(그다음 메시지에는 👀만 달려요). Telegram이나 터미널에서 답하면 보류된 메시지가 보낸 순서대로 Claude에게 가고, 그 답장은 지워져요. 요청이 열린 채로 세션이 끝나면 메시지는 🤷와 함께 버려져요.
- (팀 모드) 다른 사람의 메시지가 아직 가져가지기를 기다리고 있어요.
- (팀 모드) 다른 사람의 작업이 실행 중이에요. 그 작업이 시작한 백그라운드 에이전트도 포함해요. 터미널에서 입력한 작업은 소유자의 것으로 세니까, 소유자의 메시지(또는 개인 채팅이라면 그 채팅 자신의 멤버 메시지)만 거기에 합쳐져요.
뒤의 두 경우는 서로 다른 사람의 메시지가 한 작업에 들어가지 않게 해서, 메시지마다 자기 보낸 사람의 권한으로 실행되게 해요. 그래도 메시지가 합쳐지면(예: 절대 보류되지 않는, aipager가 모르는 보낸 사람) 그 작업은 그 안의 모든 사람 가운데 가장 엄격한 규칙으로 실행돼요(그룹 참고). 메시지가 기다리던 작업이 멈추거나 금지된 도구 때문에 막히면 보류된 메시지는 🤷와 함께 버려져요.
세션마다 최대 50개를 보류하고, 24시간 뒤에 만료돼요. /clearqueue는 이것들을 Claude가 들고 있는 모든 것과 함께 버려요.
대기 중인 메시지를 지금 보내기#
Claude가 내 작업을 하는 동안 보낸 메시지는 바로 Claude에게 가고(aipager가 보류하지 않아요), 지금 단계가 끝날 때까지 Claude의 대기열에서 기다려요. Claude의 대기열에 그것이 보이자마자 aipager가 아래에 답해요:
⏳ 대기 중 - Claude가 이 단계 다음에 읽어요
[⚡ 지금 보내기]
대기 중인 메시지마다 자기 줄이 있고, Claude가 그 메시지를 가져가자마자 그 줄이 👍와 함께 사라져요. 대기열 기록이 보이기 전에 Claude가 가져간 메시지는 줄이 없고, 👀 다음 👍만 있어요.
⚡ 지금 보내기를 누르면(또는 /now를 보내면) 그 세션에서 Claude Code 자체의 지금 보내기 단축키(Ctrl+X, 그다음 Ctrl+S)를 눌러요. 그러면 Claude는 내가 누른 메시지만이 아니라 대기열에 넣은 모든 것을 한 번에 읽어요. 진행 중이던 일이 어떻게 될지는 Claude Code가 정해요. 실행 중인 명령이나 에이전트는 백그라운드에서 계속 실행돼요. Claude가 답변을 쓰는 중이었다면 그 답변은 끊기고 내 메시지와 함께 다시 시작해요. 다른 종류의 단계(예: 다른 도구)도 짧게 기다린 뒤 중단될 수 있어요. 어떤 경우에는(예: Claude Code의 백그라운드 작업이 꺼져 있을 때) 지금 보내기가 실행 중인 단계를 평범하게 중단하는 것으로 돌아가요. 모두 Claude Code 자체의 지금 보내기 방식이고, aipager가 정하는 것이 아니에요.
누르면 지금 Claude에게 보냈어요라고 답하고 모든 줄이 함께 사라져요. 지금 보내기가 Claude에게 들고 있던 모든 것을 넘기니까요. /now도 같은 방식으로 줄을 지우고, 지금 보내기가 이미 보낸 메시지에는 줄이 없어요(그 뒤에 내가 보낸 메시지는 자기 줄이 있어요). Claude가 실행 중인 단계를 백그라운드로 옮기지 않고 취소하면 가장 오래된 대기 메시지만 바로 가져가고 나머지는 다음 단계에서 읽어요. 답변은 가져간 메시지 아래에 있고, 그 메시지가 두 번 실행되지는 않아요. 키가 Claude에 닿는 데 조금 더 걸리면(컴퓨터가 바쁠 때) 누름은 대신 Claude에게 보내는 중이라고 답해요. 그러면 키가 닿을 때 줄이 사라지거나, 닿지 못하면 줄이 남아서 ⏳ 대기 중 - 아직 Claude에 전달되지 않았어요. “지금 보내기”를 다시 눌러 주세요를 버튼과 함께 보여 줘서 다시 누를 수 있게 해요. 키는 Claude의 저장된 대화에 아직 메시지를 들고 있다고 나올 때만 눌러요. 아니면 누름은 Claude가 이미 가져갔어요라고 답하고 그 줄을 지워요. 다음 경우에는 아무 키도 누르지 않고 거부해요. 열린 권한 요청이나 질문이 있을 때(열린 질문에 먼저 답하세요), aipager가 아직 보여 주지 못한 요청을 Claude가 막 열었을 수 있을 때(이전 작업의 답변을 아직 보내는 동안에는 새 작업의 이벤트를 보류해요: 지금은 할 수 없어요. 잠시 후 다시 시도해 주세요), 그리고 aipager가 다른 메시지를 세션에 입력하는 중일 때(aipager가 메시지를 입력하는 중이에요. 잠시 후 다시 시도해 주세요)예요. 세션에 메시지를 보낼 수 있는 사람은 누구나 이 버튼과 /now를 쓸 수 있어요. read_only 멤버는 쓸 수 없어요.
줄은 그 메시지가 Claude의 대기열을 떠나면 스스로 사라져요. Claude가 가져갔거나(실행 중인 작업에 합치거나, 백그라운드 에이전트에 넘기거나, 다음 작업으로 시작하거나), 터미널의 Escape가 대기열을 입력란으로 되돌렸거나, /stop, /clearqueue, /kill, /restart, 안전 중지, 세션 종료로 버려졌을 때예요. aipager가 재시작하면 아직 보이는 모든 줄을 지워요.
이 줄들은 속도 제한 관리자를 절대 기다리지 않아요. 바쁘거나 느려진 채팅에서도, 최소 모드에서도 바로 보내고 지워요. Telegram 자체가 채팅의 보내기를 멈췄을 때만 줄을 보내지 않고(그때도 /now는 쓸 수 있어요), Telegram이 방금 aipager에게 천천히 보내라고 했다면 줄은 Telegram이 요청한 그 몇 초만 기다려요. 기다리는 메시지마다 자기 줄을 위해 채팅에 한 번 호출하고, 함께 있는 줄들은 한 번의 호출로 지워요.
파일#
올린 파일은 그 파일이 온 채팅의 비공개 폴더 /tmp/aipager-files/<chat>/에 내려받아요(<time>_<file name>으로 이름 지어요. <chat>은 채팅 ID와 봇 token으로 만든 이름이라 채팅 ID가 드러나지 않아요). 이 폴더는 aipager를 실행하는 사용자만 읽을 수 있고(0700, 파일은 0600), 한 채팅에서 실행되는 멤버의 작업은 다른 채팅에서 올린 파일을 읽거나, 검색하거나, 쓸 수 없어요(보안 참고). 이렇게 바뀌기 전에 올린 파일은 원래 자리인 /tmp/aipager-files/ 바로 아래에 그대로 있어요. 아무것도 옮기거나 지우지 않고, aipager가 시작할 때 이 폴더의 권한을 0700으로 좁혀서 그 사용자만 접근할 수 있게 해요. 봇 token을 새로 바꾸면 모든 채팅이 새 폴더를 받아요. 그전에 올린 파일은 예전 폴더에 남고, 멤버의 작업은 더 이상 그 파일을 읽을 수 없어요. 내려받을 폴더가 심볼릭 링크이거나 다른 사용자의 것이면 거부되고 업로드는 실패해요. 경로는 claude에게 줘요. 캡션이 있으면 메시지는 캡션 다음 경로이고, 캡션이 없으면 check this: <path>(앨범이면 check these: <paths>)만이라서, claude는 파일을 가리키지만 그것으로 무엇을 하라는 말은 듣지 않아요. /<label>로 시작하는 캡션은 글의 /<label> <prompt>처럼 세션을 고르고, 라벨은 메시지에서 빠져요. /api만 있으면 api에 check this: <path>를 보내고, /api compare these는 거기에 compare these <paths>를 보내요. 맨 앞의 /<label>만 대상을 골라요. 캡션 뒤쪽의 슬래시는 그냥 글이에요. 어떤 세션도 쓰지 않는 라벨은 ⚠️ 여기에는 … 이름의 세션이 없어요로 거부되고 아무것도 보내지 않아요. Telegram 봇의 20 MB 다운로드 한도를 먼저 확인해서, 너무 큰 파일은 내려받기 전에 분명하게 거부해요. 일시적인 네트워크 오류가 난 다운로드는 오류를 보여 주기 전에 모두 합쳐 최대 세 번 시도하고(1초, 그다음 2초 기다림), 오류에는 파일 이름이 적혀요. 앨범(메시지 하나로 보낸 여러 사진이나 문서)은 마지막 항목이 도착하면 메시지 하나로(캡션, 그다음 모든 파일 경로를 차례로) claude에게 넘겨요. 한 항목을 내려받지 못하면 나머지는 그래도 보내고, 빠진 항목을 적은 메모를 붙여요.
Claude가 보내는 파일#
Claude는 세션에서 파일을 보내 줄 수 있어요. 평소 말로 부탁하면 되고("스크린샷 보내 줘", "~/proj/chart.png 이미지 보내 줘"), 필요하다고 생각하면 Claude가 알아서 보내기도 해요. 이때 Claude Code의 SendUserFile 도구(Claude Code 2.1.301 이상)를 쓰는데, aipager가 시작하는 모든 세션이 이 도구를 받아요. aipager는 세션을 CLAUDE_CODE_ATTACHMENTS_BY_PATH=1로 시작하고 Claude에게 도구가 있다고 알려 줘요. aipager에 이 기능이 생기기 전에 시작한 세션은 다시 시작해야 도구를 받아요: /restart, 세션을 다시 시작하는 모드 전환, 또는 이어하기.
- 파일은 작업의 답변 다음에 그 답변에 대한 답장으로 와요(작업에 답변이 없으면 보낸 메시지에 대한 답장으로). 알림 소리 없이, 그 세션의 채팅에만 와요. 작업이 진행되는 동안 작업 카드에
📎 파일 2개 보내는 중이 보여요. - 파일은 이름이 아니라 내용에 따라 보내져요. 이미지는 Telegram의 사진 한도에 맞으면 사진으로(최대 10MB, 가로와 세로의 합 최대 10,000, 긴 변이 짧은 변의 최대 20배), 아니면 파일로 가요. GIF는 애니메이션, MP4는 동영상, MP3나 M4A는 오디오, 나머지는 모두 파일로 가요. Claude가 일반 첨부를 원하면 모두 파일로 가요.
- 사진과 동영상 2~10개는 앨범 하나로 가요. 파일은 파일끼리, 오디오는 오디오끼리 묶이고, 애니메이션은 따로 가요. 그보다 많으면 고르게 나뉘고(11개는 6개와 5개로), 앨범 하나는 50MB를 넘지 않아요.
- 캡션은
💬 <session>이고, 첫 메시지의 다음 줄에 Claude가 쓴 캡션이 붙어요(최대 1,024자). - 모든 파일은 owner를 포함해 누구에게나 먼저 확인을 거쳐요(보안). aipager 설정, Claude 자격 증명, 키, 비밀처럼 보이는 파일은 절대 보내지 않아요. 파일은 일반 파일이어야 하고, 비어 있지 않고, 50MB 이하여야 해요. 멤버는 채팅의 루트 폴더 안에 있는 파일만 보낼 수 있어요. 요청의 파일 하나가 거부되면 그 요청에서는 아무것도 보내지 않아요. Claude가 이유를 듣고 알려 주며, 작업은 계속돼요.
- aipager는 Claude Code가 파일을 읽은 순간에 만든 비공개 사본을 보내요. 사본은
~/.local/state/aipager/outbox/에 보관되고, 보내면 바로, 아니면 하루 뒤에 지워져요. - aipager는 자체 개수 제한을 두지 않아요. 보내는 속도는 Telegram의 속도 제한에 맞추고, 앨범은 파일 하나당 하나로 셈해요. 채팅이 음소거된 동안에는 파일이 기다렸다가 답변 다음에 와요.
- 보내지 못한 파일은 채팅에 한 줄로 알려 줘요. 예:
⚠️ report.pdf 파일을 보내지 못했어요: Telegram이 거부했어요.Claude 자신의 대화 기록에서 aipager가 요청을 찾지 못한 파일은 채팅에 아무 줄도 남기지 않아요(보안). 요청은 대화 기록에 있지만 aipager가 진행 중인 작업과 연결하지 못한 파일은⚠️ report.pdf 파일을 보내지 못했어요: Claude가 이번 작업에서 보낸 파일인지 aipager가 확인하지 못했어요.를 받아요. Claude가 작업하는 동안 보내서 Claude가 다음으로 가져간 메시지와, Stop hook이 계속 이어 가게 하는 작업(/goal)에서도 파일이 와요. 보내거나 거부되거나 실패한 파일마다 감사 로그에 기록이 따로 남아요.
음성 메시지#
음성 메시지는 faster-whisper(추가 패키지 aipager[voice])를 거쳐요. 소리는 이 컴퓨터에서 글로 바뀌고, 🎙️ 들은 내용: …으로 되풀이되며, 내가 입력한 것처럼 입력돼요. /new의 이름이나 기다리는 이름 바꾸기로 쓰일 때도요. Telegram 봇의 20 MB 다운로드 한도를 넘는 음성 메시지는 거부돼요. 다음에 무슨 일이 일어나는지는 hook → UserPromptSubmit을 보세요.
명령줄: 모든 명령#
내 컴퓨터에서 같은 목록을 보려면 aipager <command> --help를 실행하세요.
| 명령 | 하는 일 |
|---|---|
aipager config | 대화형 설정 마법사: 봇 token, 채팅, 멤버와 역할, Claude Code hook. 무엇이든 바꾸려면 다시 실행하세요(그룹 참고). |
aipager setup | 같은 첫 설정을 질문 없이 해요. 스크립트와 코딩 에이전트용이에요(아래 참고). |
aipager start | aipager를 포그라운드에서 실행해요. 다른 aipager가 실행 중이면 시작을 거부해요. |
aipager service install [--yes] | aipager를 백그라운드 서비스로 설치하고(Linux에서는 systemd 사용자 서비스, macOS에서는 launchd 에이전트) 시작해요. --yes는 설치된 서비스 파일이 다를 때 질문을 건너뛰어요. |
aipager service start|stop|status|logs|uninstall | 서비스를 시작하거나, 멈추거나, 보거나, 로그를 따라가거나, 제거해요. |
aipager status [--json] | aipager와 모든 세션을 한눈에: 상태, 모델, 컨텍스트, 비용, Telegram 속도 제한 상태. |
aipager logs [-f] [-n LINES] | aipager의 로그(Linux에서는 journald, macOS에서는 launchd 로그): 마지막 100줄, 또는 -n줄. -f는 계속 따라가요. |
aipager doctor [--fix] [--safety-check] [--json] | 상태 확인(문제 해결 참고). --fix는 Claude 로그인 정보와 애매한 claude 설치를 먼저 물어본 뒤 고치자고 제안해요. --safety-check는 지금 적용 중인 안전 규칙을 출력해요. --json은 스크립트용이에요(아래 참고). |
aipager session <name> [claude args] | 지금 폴더에서 dtach 아래에 <name>이라는 Claude Code 세션을 시작하거나, 실행 중이면 거기에 연결해요. 이름 뒤(또는 -- 뒤)의 인수는 그대로 claude에 넘어가요. 예: --resume. 실행 중인 aipager가 필요해요. |
aipager session ls [-a] [--json] | 세션을 나열해요. -a는 종료된 세션도 포함해요. aipager session list도 돼요. |
aipager session kill <name> [-y] | 세션을 종료해요. -y가 없으면 먼저 물어봐요. |
aipager resume [name] | 종료된 세션을 대화와 함께 되살려요. 이름이 없으면 선택기를 보여 줘요. |
aipager miniapp enable [--port PORT] [--url URL] | Mini App을 127.0.0.1:PORT(기본값 8765)에서, 관리형 터널이나 내 https:// 주소 뒤에서 켜요. 다음 aipager 재시작 때 적용돼요. |
aipager miniapp disable|status | 끄거나(다음 재시작 때), 설정과 주소를 보여 줘요. |
aipager policy validate | policy.yaml(과 policy.d)을 확인해요. 문제가 있으면 0이 아닌 코드로 끝나요. |
aipager update | aipager를 설치한 설치 프로그램(uv, pipx, Homebrew, pip)으로 업그레이드해요. aipager를 절대 재시작하지 않아요. 직접 재시작하거나, 그것을 할 수 있는 Telegram의 /update를 쓰세요(업데이트 참고). |
aipager report [--note TEXT] | 문제 신고 보고서를 보여 주고, 동의하면 보내요(아래 참고). |
aipager uninstall [-y] | aipager와 그 설정을 멈추고 제거하고, 데이터는 남겨요(아래 참고). |
aipager version, aipager --version | 버전을 출력해요. |
aipager help [command] | aipager나 명령 하나의 도움말. |
명령줄: aipager setup (코딩 에이전트용)#
위의 부분은 Telegram 봇을 설명해요. 이 부분은 내가 터미널에서 실행하거나, 내 스크립트나 코딩 에이전트가 나 대신 실행하는 명령을 설명해요. aipager setup은 질문 없이 aipager를 설정하고, aipager config를 처음 실행한 것과 같은 결과를 내요.
# 1. 사용자의 Telegram ID 찾기 (사용자가 봇을 열고 Start를 눌러요)
aipager setup detect-chat --token-file ~/bot-token.txt --json
# 2. 사용자가 본인이라고 확인하면 aipager 설정
aipager setup --token-file ~/bot-token.txt --chat-id 123456789 --service --json
# 3. 설치 확인
aipager doctor --json
이 순서로 해요: 봇 token을 읽고, Telegram에서 token을 확인하고(getMe), 아무것도 쓰지 않은 채 이 컴퓨터의 모든 것을 확인하고(기존 설정, 의존성, ~/.claude/settings.json, 실행 중인 aipager가 있는지), 사용자에게 테스트 메시지("aipager is set up for you. ...")를 보내고, 그다음에야 써요. 쓰는 파일은 설정 마법사가 쓰는 것과 같아요:
~/.config/aipager/aipager.yaml(권한 0600):bot_token, 그리고 유일한 멤버가 사용자인 개인 채팅 scope(label: owner, owner 또는 admin 역할).~/.claude/settings.json: aipager의 hook과 statusLine(기존 파일은 바뀔 때settings.json.bak.<time>으로 백업해요).- 누군가 소유자가 되면 감사 로그에
grant-owner기록이 남아요. --service가 있으면 백그라운드 서비스(aipager service install --yes와 같아요).- 예전
config.env설치에서만(아직aipager.yaml이 없을 때), 설정 마법사가 하는 변환:config.env.bak.<time>사본(team.yaml이 있으면team.yaml.bak.<time>도), 그리고 아직 없으면 예시~/.config/aipager/policy.yaml.config.env자체는 그대로 둬요.
확인 하나라도 실패하면 아무것도 보내지 않고 아무것도 쓰지 않아요. aipager가 이미 실행 중이면 채팅이나 역할 변경은 재시작 없이 다시 불러와요. 새 봇 token은 재시작이 필요하고(aipager service stop, 그다음 aipager service start), setup은 이것을 절대 직접 하지 않으며, --service는 무시돼요. setup이 실행 중인 aipager가 있는지 알 수 없으면 있다고 보고 행동해요(경고 daemon_unknown). 그때는 서비스를 절대 설치하지 않으니 실행 중인 aipager를 절대 재시작할 수 없고, 어떤 변경 뒤에도 다시 불러오기 지시를 보내지 않고 restart_needed: true를 알려요.
aipager setup 옵션#
| 옵션 | 의미 |
|---|---|
--token-file PATH | 이 파일에서 봇 token을 읽어요(최대 4096바이트. token 주위에 다른 글이 있어도 괜찮아요). 다른 사용자가 읽을 수 있는 파일도 쓸 수 있지만 token_file_shared 경고가 나와요. chmod 600하거나, setup이 성공한 뒤 지우세요. |
--token-stdin | stdin에서 봇 token을 읽어요(파이프나 리디렉션한 파일이고, 터미널은 절대 아니에요). 파이프는 닫혀야 해요. setup은 stdin이 끝나기를 최대 30초 기다린 뒤 종료 코드 2와 token_stdin_timeout으로 끝나요. printf '%s\n' "$TOKEN" | aipager setup --token-stdin ...과 < token.txt는 둘 다 파이프를 닫아요. |
--chat-id N | 필수. 그 사람의 숫자 Telegram 사용자 ID이고, 그 사람 개인 채팅의 ID이기도 해요. 그룹은 aipager config로 추가해요. |
--role owner|admin | 그 사람의 역할(기본값 owner). |
--service | 백그라운드 서비스도 설치하고 시작해요(aipager가 실행 중이면 무시돼요). |
--force | 기존 설치의 봇 token, 소유자의 개인 채팅, 역할을 바꿀 수 있게 해요. 그룹과 다른 개인 채팅의 설정은 그대로 둬요. |
--dry-run | 모든 확인을 실행하고, 아무것도 보내지 않고, 아무것도 쓰지 않고, 무엇이 바뀔지 알려 줘요. |
--json | stdout에 JSON 객체를 딱 하나 출력해요. 나머지는 모두 stderr로 가요. |
--token-file과 --token-stdin 가운데 정확히 하나를 주세요. token은 옵션 값으로 절대 받지 않아요. --token과 --bot-token은 거부되고(token_on_command_line) 값은 보여 주지 않아요. 줄인 옵션(--token-f, --dry)은 받지 않아요. setup은 절대 질문하지 않고, 터미널이 필요 없으며, 환경 변수에서 token을 읽지 않아요.
같은 token, 채팅, 역할로 다시 실행하면 아무것도 바꾸지 않고 그렇다고 알려 줘요(unchanged, 테스트 메시지 없음). 기존 설치에 다른 token, 채팅, 역할을 쓰면 --force를 붙일 때까지 종료 코드 6으로 끝나요. 예전 config.env 설치는 설정 마법사처럼 먼저 변환해요(그것은 admin 역할을 주니까, 그런 설치에서 --role owner는 --force가 필요해요).
aipager setup detect-chat#
aipager setup detect-chat (--token-file PATH | --token-stdin) [--timeout SECONDS] [--json]
봇에게 온 가장 최근 개인 메시지를 기다리고(Start를 누르면 하나가 가요) 보낸 사람을 출력해요. ID, 이름, 사용자 이름이에요. 아무것도 저장하지 않으니, 그 사람을 사용자에게 보여 주고 사용자가 확인한 뒤에만 aipager setup --chat-id를 실행하세요. 명령이 시작되기 전 10분 안에 보낸 메시지만 세어요. --timeout은 0~3600초예요(기본값 300. 0은 한 번만 본다는 뜻이에요). 메시지를 확인 처리하지 않고 Telegram을 읽으니까, /start는 설정 마법사와 aipager가 쓸 수 있게 남아 있어요.
같은 봇의 aipager가 실행 중이면 그 aipager가 봇의 메시지를 받고, 아직 설정되지 않은 사람의 개인 메시지는 기록하지 않아요. 그러면 detect-chat은 종료 코드 9(source: "daemon")로 끝나요. aipager를 멈추고(aipager service stop) 다시 실행하거나, 사용자에게 숫자 ID를 물어보세요. 다른 봇의 aipager라면 Telegram을 직접 읽어요(source: "telegram").
한계: detect-chat은 메시지를 절대 확인 처리하지 않으니, Telegram은 지난 24시간의 읽지 않은 업데이트 가운데 가장 오래된 100개만 보여 줘요. 봇에 그만큼 쌓여 있으면 새 /start는 보일 수 없어요. 그때 detect-chat은 updates_backlog 경고와 함께 시간 초과(종료 코드 8)로 끝나고, Start를 다시 눌러도 소용없어요. 대신 사용자에게 숫자 Telegram ID를 물어보세요(또는 aipager를 한 번 시작하면 쌓인 것을 읽어서 비워요).
종료 코드#
| 코드 | error | 의미 |
|---|---|---|
| 0 | 성공(installed, updated, unchanged, dry_run, 또는 detect-chat의 found). | |
| 1 | config_malformed, ambiguous_install, settings_invalid, test_send_failed, write_failed, service_failed, internal_error | 실패. ambiguous_install: 소유자의 것일 수 있는 개인 채팅이 둘 이상이에요. aipager config에서 고치세요. |
| 2 | usage, missing_token_source, token_source_conflict, token_file_unreadable, token_stdin_is_tty, token_stdin_timeout, token_malformed, token_on_command_line, bad_chat_id, bad_role, bad_timeout | 입력이 잘못됐거나 빠졌어요. fix가 어떤 옵션인지 알려 줘요. 모르는 옵션이나 setup 뒤의 잘못된 단어는 인수 분석기가 알려 주고(일반 텍스트, JSON 없음), 이것도 코드 2예요. |
| 3 | deps_missing | dtach, claude, aipager-hook, aipager-statusline이 빠졌어요. fix에 실행할 명령이 있어요. 아무것도 쓰지 않아요. |
| 4 | token_rejected | Telegram이 token을 거부했어요. |
| 5 | chat_not_started, bot_blocked | 사용자가 봇에서 아직 Start를 누르지 않았거나("Open t.me/<bot> and press Start, then run this again."), 봇을 차단했어요. 아무것도 쓰지 않아요. |
| 6 | existing_install | 다른 token, 채팅, 역할이 이미 설정되어 있어요. --force를 붙이세요. |
| 7 | telegram_unreachable | 네트워크 오류, 또는 Telegram이 429나 5xx로 답했어요. |
| 8 | detect_timeout | detect-chat이 --timeout이 끝나기 전에 개인 메시지를 하나도 보지 못했어요(오래된 읽지 않은 업데이트 100개가 더 새 것을 가렸을 때는 updates_backlog 경고와 함께). |
| 9 | daemon_running | detect-chat: 실행 중인 aipager가 봇의 메시지를 받고 있어요. |
| 10 | updates_conflict | detect-chat: 다른 프로그램이 이 봇의 메시지를 읽고 있어요. |
| 130 | interrupted | Ctrl-C. |
여러 확인이 함께 실패하면 종료 코드는 이 순서를 따라요: config_malformed, ambiguous_install, existing_install, settings_invalid, deps_missing. --dry-run은 실제로 실행했을 때 이 확인들이 낼 코드로 끝나요.
JSON: aipager setup --json#
모든 키가 항상 있어요. 모르는 값은 null이에요.
{
"command": "setup",
"status": "installed",
"ok": true,
"exit_code": 0,
"error": null,
"message": "aipager is set up for @example_bot (chat 123456789, role owner). ...",
"fix": null,
"dry_run": false,
"bot_username": "example_bot",
"chat_id": 123456789,
"role": "owner",
"changed": ["bot_token", "owner_dm", "settings_json"],
"test_message": "sent",
"deps": {"ok": true, "items": [{"name": "dtach", "found": true, "path": "/usr/bin/dtach", "fix": null}]},
"settings_json": {"path": "/home/you/.claude/settings.json", "status": "created", "backup": null, "repointed": 0},
"daemon": {"running": false, "reload": "not_needed", "restart_needed": false},
"service": {"requested": false, "result": "not_requested"},
"warnings": [],
"next_step": "Start aipager with `aipager service install` ..."
}
status:installed,updated,unchanged,dry_run,error.changed: 바뀐 것(시험 실행이면 바뀔 것), 이 순서로:migrated_v1,bot_token,owner_dm,role,settings_json,service.test_message:sent,not_needed,skipped_dry_run,failed,not_attempted.settings_json.status:created,patched,unchanged,would_change,not_checked.daemon.reload:reloaded,refused,not_reloaded,not_needed.restart_needed는 aipager가 실행 중일 때 새 token을 쓴 뒤, 그리고 aipager가 다시 불러올 수 없거나 setup이 실행 중인 aipager가 있는지 알 수 없을 때의 모든 변경 뒤에 true예요.service.result:not_requested,installed,already_installed,skipped_daemon_running,would_install,failed, 또는 setup이 그 단계 전에 멈췄으면null.warnings:code(token_file_shared,reload_refused,daemon_unknown,audit_write_failed)와message가 있는 객체.audit_write_failed: 소유자 권한은 썼지만, 그grant-owner기록을 감사 로그에 더하지 못했어요. 기록은 최선의 노력이라서 종료 코드는 여전히 0이에요.- aipager가 실행 중인데 다시 불러올 수 없는 변경 뒤의
next_step은 재시작하라고 해요(aipager service stop, 그다음aipager service start). 봇 token은 token이 바뀌었을 때만 언급해요. daemon_unknown경고가 있으면next_step은 aipager가 실행 중이라고 절대 말하지 않아요. setup이 알 수 없었다고 말하고,aipager doctor --json을 가리키고, aipager가 실행 중이 아니면aipager service install이나aipager start로 시작하라고 해요. 재시작이 필요하면 실행 중인 aipager가 있을 때만 재시작을 요청하고, 아니면 같은 방법으로 시작하라고 해요.- 오류가 나면
status는error이고,error,message,fix가 무엇이 잘못됐고 무엇을 해야 하는지 알려 줘요.next_step은fix를 되풀이해요. 쓰기 뒤에 오류가 나면changed가 이미 쓴 것을 나열하고, 출력에 보통 stderr 한 줄이 더해져요:Already written: aipager.yaml, ... (changed: ...).
JSON: aipager setup detect-chat --json#
{
"command": "detect-chat",
"status": "found",
"ok": true,
"exit_code": 0,
"error": null,
"message": "Found Ada L (@ada), Telegram id 123456789.",
"fix": null,
"bot_username": "example_bot",
"source": "telegram",
"candidate": {"id": 123456789, "first_name": "Ada", "last_name": "L", "username": "ada", "date": 1700000000},
"other_candidates": 0,
"warnings": [],
"next_step": "Show this person to the user; once they confirm, run: aipager setup --token-file <path> --chat-id 123456789"
}
status가 found가 아니면 candidate는 null이에요. warnings에는 code(token_file_shared, updates_backlog)와 message가 있는 객체가 들어 있어요. other_candidates는 같은 10분 동안 봇에게 메시지를 보낸 다른 사람 수예요. 0이 아니면 맞는 사람을 골랐는지 꼭 확인하세요. next_step은 내가 쓴 token 옵션을 되풀이하고, token 자체는 절대 되풀이하지 않아요.
aipager doctor --json#
aipager doctor의 확인을 stdout에 JSON 객체 하나로 보여 줘요. 보통의 aipager doctor처럼 읽기만 하고 Telegram 메시지를 보내지 않아요. 실패한 확인이 있으면 종료 코드 1, 아니면 0으로 끝나요. --fix나 --safety-check와 함께 쓸 수 없어요(종료 코드 2와 오류 객체로 끝나요).
{
"command": "doctor",
"version": "1.0.1",
"ok": true,
"summary": {"ok": 14, "warn": 1, "fail": 0},
"checks": [{"key": "token_valid", "status": "ok", "title": "Telegram bot token", "detail": ["@example_bot"], "fix": null}]
}
checks는 이 순서로, 이 키를 써요: config_parses, config, token_valid, chat_reachable, team, role_shell_access, peer_messages, claude, claude_auth, dtach, hook_scripts, settings_json, daemon, service_installed, service_unit_path, miniapp. 비정상 종료한 확인도 키를 유지하고 상태는 warn이에요. 글은 일반 텍스트이고(서식 없음), 봇 token처럼 보이는 것은 모두 <redacted>로 바뀌어요.
명령줄: aipager uninstall#
aipager uninstall은 aipager를 멈추고 서비스를 제거하고, 설정 폴더(~/.config/aipager: 봇 token, 채팅, 설정)와 세션 목록(~/.claude/aipager-sessions.json)을 지우고, /tmp의 aipager 파일을 정리하고, aipager 자체를 제거해요. 먼저 물어봐요(-y는 질문을 건너뛰어요).
다시 설치하면 이어서 쓸 수 있도록 aipager의 데이터는 남겨요. ~/.local/share/aipager(세션 폴더, 내려받은 보조 프로그램, 잠금 파일과 표시 파일), ~/.local/state/aipager(문제 신고 기록과 언어 상태), ~/.claude/aipager-audit.jsonl(권한 요청에 대한 답의 로그), ~/.claude/aipager-flood-state.json(Telegram 속도 제한 기록), ~/.claude/aipager-pending-users.json(권한 없이 봇을 써 보려 한 사람), ~/aipager-users(멤버의 루트 폴더와 그 작업)예요. 묻기 전에 내 컴퓨터에 이런 것이 무엇이 있는지 나열하고, 마지막에 이것들까지 지우는 rm -rf 명령을 출력해요.
Claude Code의 ~/.claude/settings.json에서 aipager 자체 항목도 지워요. aipager-hook을 실행하는 hook과 aipager-statusline을 실행하는 상태 줄이에요(내 상태 줄은 유지해요). 그 파일의 다른 것은 모두 그대로이고, 이전 상태의 파일은 옆에 settings.json.bak.<time>으로 남겨요. 파일이 다른 파일로 가는 링크이거나, 읽을 수 없거나, 올바른 JSON이 아니거나, Claude Code가 읽는 형식이 아니면 손대지 않고, uninstall이 어떤 항목을 직접 지워야 하는지 알려 줘요. aipager-hook이나 aipager-statusline을 언급하는 내 hook(예: 감싸는 스크립트 aipager-hook-capped.sh)도 유지하고, uninstall이 그 이름을 알려 줘요. 열려 있는 Claude Code 세션은 재시작할 때까지 이미 불러온 hook을 계속 실행해요. 다시 설치하면 aipager config가 이 항목을 다시 더해요. 내 Telegram 봇과 예전 settings.json.bak.* 백업은 건드리지 않고, skipDangerousModePermissionPrompt(권한 확인을 끈 채 실행하기 전 Claude Code의 경고를 건너뛰는 설정)도 마찬가지예요. aipager가 설정하지만 나도 직접 설정했을 수 있으니까요.
명령줄: aipager report#
aipager report는 aipager가 이 컴퓨터에 기록한 것으로 봇의 “문제 신고” 버튼과 같은 종류의 문제 신고 보고서를 만들고(실행 중인 aipager에 대한 정보는 더 적어요. 모드, 채팅, 기능, 실행 시간, 세션, Claude Code 세부 정보가 비어 있거나 알 수 없음으로 나와요), 전체를 출력하고, Send this report? [y/N]을 물어요. y나 yes만 보내요. 다른 답이나, 터미널이 없을 때(스크립트, 파이프, Claude Code 세션의 shell)는 아무것도 보내지 않아요. --note "text"는 최대 500자의 메모를 더하고, 출력된 보고서에 보여요. 보고서를 보냈거나, 내 선택으로 보내지 않았거나, 출력만 했으면 종료 코드 0으로, 내가 확인한 보내기가 나가지 못했으면 1로 끝나요. 보고서에 무엇이 들어 있고 어디로 가는지는 문제 신고를 보세요.
환경 변수로 하는 설정#
몇몇 설정은 aipager.yaml이 아니라 aipager의 환경에서 읽어요. systemd 서비스라면 systemctl --user edit aipager로 [Service] 아래에 추가하고(Environment=STALE_BUSY_TIMEOUT=900), aipager service stop과 aipager service start를 하세요. macOS의 launchd 서비스라면 plist 파일의 EnvironmentVariables에 추가하세요(~/Library/LaunchAgents/com.aipager.daemon.plist. aipager service install이 이 파일을 다시 쓰니까, 그것을 실행한 뒤에는 다시 추가하세요). 내가 직접 시작한 aipager라면 aipager start 전에 설정하세요. 보통 쓸 때는 이 설정이 하나도 필요 없어요.
| 설정 | 기본값 | 무엇을 바꾸나요 |
|---|---|---|
AIPAGER_WHISPER_MODEL | base | 음성 메시지의 음성 인식 모델(tiny, base, small, medium, ...). 클수록 정확하고 느려요. |
MINIAPP_PORT | 8765 | 127.0.0.1에서 Mini App의 포트(aipager miniapp enable --port는 이것을 저장해요). |
MINIAPP_ENABLED | aipager.yaml을 따름 | 0은 Mini App을 끄고 1은 켜요. aipager.yaml이 뭐라고 하든요(aipager miniapp enable|disable은 선택을 저장해요). |
MINIAPP_PUBLIC_URL | 없음 | 관리형 터널 대신 내 https:// 주소에서 Mini App을 제공해요(보안). |
KEEP_FINISHED_CARD | 1 | 0은 /settings에서 배치를 한 번도 고른 적 없는 채팅의 메시지 배치를 “카드 지우고 답변만”으로 정해요. |
STALE_BUSY_TIMEOUT | 7200 (2시간) | 작업 중인 세션이 몇 초 동안 조용하면 채팅에 “아직 작업 중 (120분째 소식 없음)” 안내가 오는지. |
PROMPT_HOOK_GRACE_SECONDS | 8 | Claude Code가 끝내 가져가지 않은 메시지를 몇 초 뒤에 “Claude Code가 메시지를 받지 않았어요”로 표시하는지(자유 메시지). |
FINISH_CARD_GRACE_SECONDS | 0.8 | 카드가 있는 배치에서 끝난 카드가 답변보다 얼마나 먼저 오는지. 0은 둘을 함께 보내요. |
GONE_SESSION_MAX_AGE_DAYS | 14 | 종료된 세션이 /resume에 며칠 동안 남는지. 0은 영원히 유지해요. |
STREAM_EDIT_INTERVAL, BUSY_EDIT_INTERVAL | 1.2, 3.0 | 글이 흘러나올 때와 조용할 때 작업 카드의 새로 고침 속도, 초 단위. Telegram의 채팅마다 한도가 여전히 이겨요(문제 해결). |
TYPING_INDICATOR_INTERVAL | 4.5 | “입력 중…” 표시 사이의 초. 0은 꺼요. |
TELEGRAM_MAX_RETRY_AFTER | 90 | Telegram이 요청한 기다림이 몇 초를 넘으면 aipager가 속도 제한을 차단으로 보는지(낮추지 마세요. 문제 해결 참고). |
CLAUDE_RICH_SUMMARIES | 1 | 0은 코드 블록이 있는 답변의 풍부한 서식을 꺼요(예전 버전에서 남겨 둔 대체 수단). |
AIPAGER_INTERACTIVE_TIMEOUT | 300 | 권한 요청이나 질문이 Claude Code의 소식 없이 몇 초 기다리면 aipager가 저장된 대화를 확인하는지. 그 대화에 요청이 끝났다고 나올 때만 세션을 다시 작업 중으로 쳐요(문제 해결). |
AIPAGER_SUBAGENT_SILENCE | 1800 | 몇 초 동안 조용하면 멈춤이 보이지 않은 서브에이전트를 버리는지(hook). |
AIPAGER_BG_SHELL_MAX_TRACK | 7200 | 몇 초 뒤에 끝나는 것이 보이지 않은 백그라운드 명령을 더 세지 않는지. |
AIPAGER_REPORT_PROMPTS | 켜짐 | 0은 문제 신고 보고서를 보내자는 자동 제안을 꺼요(문제 신고). |
AIPAGER_CLAUDE_BIN | 없음 | 여러 개가 설치되어 있을 때 실행할 claude 프로그램(aipager.yaml의 claude_path가 이겨요). |
AIPAGER_SOCKET_PATH | $XDG_RUNTIME_DIR/aipager.sock | aipager의 제어 소켓. aipager가 시작한 세션은 이것을 자기 hook에 넘겨요. |
AIPAGER_DEBUG | 꺼짐 | 세션 환경에서 1이면 aipager-hook과 aipager-statusline이 진단 정보를 stderr에 출력해요. |
AIPAGER_INSTANCE_DIR, AIPAGER_TELEGRAM_API_BASE | 없음 | 테스트용 두 번째 격리된 aipager(아키텍처). |