Skip to content

channprj/kmsg

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

97 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

kmsg

kmsg logo

Disclaimer: kmsg๋Š” Kakao Corp. ์˜ ๊ณต์‹ ๋„๊ตฌ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ๋ณธ์ธ ๊ณ„์ •/ํ™˜๊ฒฝ์—์„œ ๊ด€๋ จ ๋ฒ•๊ทœ, ์„œ๋น„์Šค ์•ฝ๊ด€, ํšŒ์‚ฌ ๋ณด์•ˆ ์ •์ฑ…์„ ์ค€์ˆ˜ํ•  ์ฑ…์ž„์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋„๊ตฌ ์‚ฌ์šฉ์œผ๋กœ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๊ณ„์ • ์ œํ•œ, ์˜ค์ž‘๋™, ๋ฐ์ดํ„ฐ ์†์‹ค, ๊ธฐํƒ€ ์†ํ•ด์— ๋Œ€ํ•œ ์ฑ…์ž„์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ์žˆ์Šต๋‹ˆ๋‹ค. LOCO Protocol ์ด ์•„๋‹Œ AX ๋ฅผ ์‚ฌ์šฉํ•œ ์ด์œ ์™€ ๊ณ„์ • ์ œ์žฌ ๊ฐ€๋Šฅ์„ฑ์— ๋Œ€ํ•œ ์ œ ๊ฐœ์ธ์ ์ธ ํŒ๋‹จ์€ ์™œ KakaoTalk ์˜ LOCO Protocol ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋‚˜์š”? ํ•ญ๋ชฉ์„ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

kmsg ๋Š” macOS์—์„œ ์นด์นด์˜คํ†ก ๋ฉ”์‹œ์ง€๋ฅผ CLI ๋กœ ์ฝ๊ณ  ๋ณด๋‚ด๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ๋‹จ์ˆœํ•œ ์ˆ˜๋™ CLI ๋ฅผ ๋„˜์–ด, AI Agent ๋˜๋Š” Hook ์ด๋ฒคํŠธ ๋“ฑ์˜ ์ž๋™ํ™” ํŒŒ์ดํ”„๋ผ์ธ์— ์—ฐ๊ฒฐํ•˜๊ธฐ ์‰ฝ๋„๋ก ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. kmsg ๋Š” openclaw ์˜ ์ฐฝ์‹œ์ž์ธ steipete ๊ฐ€ ๋งŒ๋“  iMessage ์ปจํŠธ๋กค์„ ์œ„ํ•œ CLI ๋„๊ตฌ์ธ imsg ์— ์˜๊ฐ์„ ๋ฐ›์•„ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Demo

record.mp4

๋น ๋ฅธ ์‹œ์ž‘

์š”๊ตฌ์‚ฌํ•ญ:

์„ค์น˜ (Homebrew ๊ถŒ์žฅ)

brew install channprj/tap/kmsg

ํŠน์ • ๋ฆด๋ฆฌ์ฆˆ๋ฅผ ๊ณ ์ •ํ•ด์„œ ์„ค์น˜ํ•˜๋ ค๋ฉด exact version formula๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. tap์—๋Š” ์ตœ์‹  10๊ฐœ ๋ฆด๋ฆฌ์ฆˆ๋งŒ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

brew install channprj/tap/kmsg@2026.0422.22

์„ค์น˜ (์ง์ ‘ ๋‹ค์šด๋กœ๋“œ)

mkdir -p ~/.local/bin && curl -fL https://github.com/channprj/kmsg/releases/latest/download/kmsg-macos-universal -o ~/.local/bin/kmsg && chmod +x ~/.local/bin/kmsg

์„ค์น˜ ํ™•์ธ์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

kmsg status

๊ถŒํ•œ ํŒ์—…์ด ๋œจ๋ฉด ํ—ˆ์šฉํ•ด ์ฃผ์„ธ์š”.

๊ฐ€์žฅ ๋งŽ์ด ์“ฐ๋Š” ๋ช…๋ น

kmsg status
kmsg auth login
kmsg auth login --auto
kmsg -v
kmsg send "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" "์•ˆ๋…•ํ•˜์„ธ์š”"
kmsg send --chat-id "chat_7f42c5e1d9ab" "์•ˆ๋…•ํ•˜์„ธ์š”"
kmsg send "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" "$(date '+%Y-%m-%d %H:%M:%S') ํ…Œ์ŠคํŠธ"
kmsg send "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" "ํ…Œ์ŠคํŠธ" --keep-window
kmsg send-image "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" "/path/to/image.png"
kmsg mcp-server
kmsg chats
kmsg chats --json
kmsg read "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" --limit 20
kmsg read "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" --limit 20 --keep-window
kmsg read "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" --limit 20 --json
kmsg read "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" --limit 20 --deep-recovery
kmsg watch "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„"
kmsg watch "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" --json
kmsg watch "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" --json --poll-interval 0.5
kmsg inspect --window 0 --depth 20 --debug-layout

CLI ๋ช…๋ น์–ด ๋ ˆํผ๋Ÿฐ์Šค

์˜ต์…˜์€ ๋นŒ๋“œ ๊ธฐ์ค€ kmsg --help, kmsg <command> --help ์ถœ๋ ฅ๊ณผ ๋™์ผํ•˜๊ฒŒ ๊ด€๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

status

kmsg status [--verbose]
  • --verbose: ์ƒ์„ธ ์ƒํƒœ ์ถœ๋ ฅ

status, chats, read, send, send-image, watch, cache warmup์€ ์นด์นด์˜คํ†ก ๋กœ๊ทธ์ธ์ด ํ’€๋ ค ์žˆ์œผ๋ฉด ์ €์žฅ๋œ ์ž๊ฒฉ ์ฆ๋ช…์œผ๋กœ ์ž๋™ ๋กœ๊ทธ์ธ์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. ์ €์žฅ๋œ ์ •๋ณด๊ฐ€ ์—†๊ฑฐ๋‚˜ ๋ถˆ์™„์ „ํ•˜๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ ์•„์ด๋””/๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅ๋ฐ›์•„ ~/.config/kmsg/credentials.json์— ์ €์žฅํ•˜๊ณ , ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ‚ค๋Š” ~/.config/kmsg/credentials/์— ๋ณ„๋„๋กœ ๋ณด๊ด€ํ•ฉ๋‹ˆ๋‹ค.

auth login

kmsg auth login [--auto] [--trace-ax]
  • --auto: ์ €์žฅ๋œ ์ž๊ฒฉ ์ฆ๋ช…์ด ์žˆ์œผ๋ฉด ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ณ , ์—†์œผ๋ฉด ์ž…๋ ฅ๋ฐ›์•„ ์ €์žฅ ํ›„ ๋กœ๊ทธ์ธ
  • --trace-ax: AX ํƒ์ƒ‰/์žฌ์‹œ๋„ ๋กœ๊ทธ ์ถœ๋ ฅ

๊ธฐ๋ณธ kmsg auth login์€ ์•„์ด๋””/๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ƒˆ๋กœ ์ž…๋ ฅ๋ฐ›์•„ ์ €์žฅํ•˜๊ณ  ๋กœ๊ทธ์ธํ•ฉ๋‹ˆ๋‹ค. ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ํ‰๋ฌธ์ด ์•„๋‹ˆ๋ผ ์•”ํ˜ธํ™”๋œ ํ˜•ํƒœ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

chats

kmsg chats [--verbose] [--limit <limit>] [--trace-ax] [--json] [--keep-window]
  • -v, --verbose: ์ƒ์„ธ ์ •๋ณด ์ถœ๋ ฅ
  • -l, --limit <limit>: ์ตœ๋Œ€ ์ฑ„ํŒ… ๋ชฉ๋ก ๊ฐœ์ˆ˜ (๊ธฐ๋ณธ๊ฐ’: 20)
  • --trace-ax: AX ํƒ์ƒ‰/์žฌ์‹œ๋„ ๋กœ๊ทธ ์ถœ๋ ฅ
  • --json: chat_id๋ฅผ ํฌํ•จํ•œ ๊ตฌ์กฐํ™” JSON ์ถœ๋ ฅ
  • -k, --keep-window: chats ์‹คํ–‰ ์ค‘ ์ž๋™์œผ๋กœ ์—ฐ ์ฐฝ์„ ์œ ์ง€

๊ธฐ๋ณธ ์ถœ๋ ฅ์—๋Š” ๊ฐ ์ฑ„ํŒ…์˜ chat_id๊ฐ€ ํ•จ๊ป˜ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. chat_id๋Š” ๋กœ์ปฌ registry(~/.kmsg/chat-registry.json)์— ์ €์žฅ๋˜๋Š” synthetic ID์ด๋ฉฐ, ์ฑ„ํŒ…๋ฐฉ ์ด๋ฆ„์„ ๊ธฐ์ค€์œผ๋กœ ์ƒ์„ฑ/์žฌ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ์ด๋ฆ„์˜ ๋ฐฉ์ด ์—ฌ๋Ÿฌ ๊ฐœ๋ฉด registry๊ฐ€ ๋ณ„๋„ ID๋ฅผ ์œ ์ง€ํ•˜๊ณ , ๋ฐฉ ์ด๋ฆ„์ด ๋ฐ”๋€Œ๋ฉด ์ƒˆ ID๋กœ ์ทจ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค. chats๊ฐ€ ์‹คํ–‰ ์ค‘ ์ฐฝ์„ ์ž๋™์œผ๋กœ ์—ด์—ˆ๋‹ค๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ข…๋ฃŒ ์‹œ ๋‹ซ๊ณ , --keep-window์ผ ๋•Œ๋งŒ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

read

kmsg read <chat> [--limit <limit>] [--debug] [--trace-ax] [--keep-window] [--deep-recovery] [--json]
  • -l, --limit <limit>: ์ตœ๋Œ€ ๋ฉ”์‹œ์ง€ ๊ฐœ์ˆ˜ (๊ธฐ๋ณธ๊ฐ’: 20)
  • --debug: raw element ๋””๋ฒ„๊ทธ ์ •๋ณด ์ถœ๋ ฅ
  • --trace-ax: AX ํƒ์ƒ‰/์žฌ์‹œ๋„ ๋กœ๊ทธ ์ถœ๋ ฅ
  • -k, --keep-window: ์ž๋™์œผ๋กœ ์—ฐ ์ฑ„ํŒ…์ฐฝ๊ณผ ๋ฆฌ์ŠคํŠธ์ฐฝ ์œ ์ง€
  • --deep-recovery: ๋น ๋ฅธ ํƒ์ƒ‰ ์‹คํŒจ ์‹œ deep recovery ์ˆ˜ํ–‰
  • --json: JSON ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅ

watch

kmsg watch <chat> [--poll-interval <seconds>] [--trace-ax] [--keep-window] [--deep-recovery] [--json] [--include-system]
  • --poll-interval <seconds>: ํด๋ง ์ฃผ๊ธฐ (๊ธฐ๋ณธ๊ฐ’: 0.2, ๋ฒ”์œ„: 0.2...10.0)
  • --trace-ax: AX ํƒ์ƒ‰/์žฌ์‹œ๋„ ๋กœ๊ทธ ์ถœ๋ ฅ
  • -k, --keep-window: ์ž๋™์œผ๋กœ ์—ฐ ์ฑ„ํŒ…์ฐฝ ์œ ์ง€
  • --deep-recovery: ๋น ๋ฅธ ํƒ์ƒ‰ ์‹คํŒจ ์‹œ deep recovery ์ˆ˜ํ–‰
  • --json: ์ƒˆ ๋ฉ”์‹œ์ง€ ์ด๋ฒคํŠธ๋ฅผ pretty JSON ๊ฐ์ฒด๋กœ ๊ณ„์† ์ถœ๋ ฅ
  • --include-system: ๋‚ ์งœ ๊ตฌ๋ถ„์„  ๋“ฑ ์‹œ์Šคํ…œ์„ฑ ๋ฉ”์‹œ์ง€๋„ ํฌํ•จ

watch๋Š” ์‹œ์ž‘ ์‹œ ํ˜„์žฌ ๋Œ€ํ™”๋ฅผ baseline์œผ๋กœ๋งŒ ์žก๊ณ  ์ถœ๋ ฅํ•˜์ง€ ์•Š์œผ๋ฉฐ, transcript๊ฐ€ ๋Šฆ๊ฒŒ ๋กœ๋”ฉ๋˜๋Š” ๊ฒฝ์šฐ๋ฅผ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ์ตœ๋Œ€ 1-2์ดˆ ์ •๋„ silent warm-up์œผ๋กœ baseline์„ ์•ˆ์ •ํ™”ํ•œ ๋’ค ๊ฐ์‹œ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ํŒŒ์‹ฑ๋œ ๋‚ ์งœ/์‹œ๊ฐ์„ ๊ธฐ์ค€์œผ๋กœ watch ์‹œ์ž‘ ์‹œ์  ์ด์ „ ๋ฉ”์‹œ์ง€๋Š” ์ถœ๋ ฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์•ˆ์ „ํ•œ ์‹œ๊ฐ์„ ์ถ”์ •ํ•  ์ˆ˜ ์—†๋Š” ํ–‰์€ ๋ณด์ˆ˜์ ์œผ๋กœ ์ˆจ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. steady-state์—์„œ๋Š” ๋งˆ์ง€๋ง‰์œผ๋กœ ์„ฑ๊ณตํ•œ transcript context๋ฅผ ์šฐ์„  ์žฌ์‚ฌ์šฉํ•˜๊ณ , transcript root๋„ AX cache happy path๋ฅผ ์‚ฌ์šฉํ•ด 200ms polling์„ ๊ฒฌ๋”œ ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ๋ชจ๋“œ๋Š” ์ผ๋ฐ˜ ๋Œ€ํ™” ๋ฉ”์‹œ์ง€๋งŒ ์ถœ๋ ฅํ•˜๊ณ , --include-system์ผ ๋•Œ๋งŒ ์‹œ์Šคํ…œ์„ฑ ๋ฉ”์‹œ์ง€๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. --json์—์„œ๋Š” ์ด๋ฒคํŠธ๋งˆ๋‹ค ๋ณ„๋„ JSON ๊ฐ์ฒด๊ฐ€ stdout์œผ๋กœ ์ถœ๋ ฅ๋˜๊ณ , --trace-ax๋Š” ๊ณ„์† stderr์—๋งŒ ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค.

send

kmsg send <recipient> <message> [--dry-run] [--trace-ax] [--no-cache] [--refresh-cache] [--keep-window] [--deep-recovery]
kmsg send --chat-id <chat-id> <message> [--dry-run] [--trace-ax] [--no-cache] [--refresh-cache] [--keep-window] [--deep-recovery]
  • --chat-id <chat-id>: kmsg chats์—์„œ ์ถœ๋ ฅ๋œ chat_id๋กœ ์ „์†ก
  • --dry-run: ์‹ค์ œ ์ „์†ก ์—†์ด ์‹œ๋ฎฌ๋ ˆ์ด์…˜
  • --trace-ax: AX ํƒ์ƒ‰/์žฌ์‹œ๋„ ๋กœ๊ทธ ์ถœ๋ ฅ
  • --no-cache: ์ด๋ฒˆ ์‹คํ–‰์—์„œ AX path cache ๋น„ํ™œ์„ฑํ™”
  • --refresh-cache: ์ด๋ฒˆ ์‹คํ–‰์—์„œ AX path cache ๊ฐ•์ œ ์žฌ๊ตฌ์„ฑ
  • -k, --keep-window: ์ž๋™์œผ๋กœ ์—ฐ ์ฑ„ํŒ…์ฐฝ๊ณผ ๋ฆฌ์ŠคํŠธ์ฐฝ ์œ ์ง€
  • --deep-recovery: ๋น ๋ฅธ ํƒ์ƒ‰ ์‹คํŒจ ์‹œ deep recovery ์ˆ˜ํ–‰

--chat-id ์ „์†ก์€ local registry์—์„œ ์ฑ„ํŒ…๋ฐฉ ์ด๋ฆ„์„ ์—ญ์กฐํšŒํ•œ ๋’ค ๊ธฐ์กด search/open ๊ฒฝ๋กœ๋กœ ์ฑ„ํŒ…์„ ์—ฝ๋‹ˆ๋‹ค. registry์— ์—†๋Š” ID๋Š” ์ฆ‰์‹œ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. send๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ „์†ก ํ›„ ์ฑ„ํŒ…์ฐฝ๊ณผ ์นดํ†ก ๋ฆฌ์ŠคํŠธ์ฐฝ์„ ์ •๋ฆฌํ•˜๊ณ , --keep-window์ผ ๋•Œ๋งŒ ๋‘˜ ๋‹ค ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

send-image

kmsg send-image <recipient> <image-path> [--trace-ax] [--no-cache] [--keep-window] [--deep-recovery]
  • --trace-ax: AX ํƒ์ƒ‰/์žฌ์‹œ๋„ ๋กœ๊ทธ ์ถœ๋ ฅ
  • --no-cache: ์ด๋ฒˆ ์‹คํ–‰์—์„œ AX path cache ๋น„ํ™œ์„ฑํ™”
  • -k, --keep-window: ์ž๋™์œผ๋กœ ์—ฐ ์ฑ„ํŒ…์ฐฝ๊ณผ ๋ฆฌ์ŠคํŠธ์ฐฝ ์œ ์ง€
  • --deep-recovery: ๋น ๋ฅธ ํƒ์ƒ‰ ์‹คํŒจ ์‹œ deep recovery ์ˆ˜ํ–‰

send-image๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ „์†ก ํ›„ ์ฑ„ํŒ…์ฐฝ๊ณผ ์นดํ†ก ๋ฆฌ์ŠคํŠธ์ฐฝ์„ ์ •๋ฆฌํ•˜๊ณ , --keep-window์ผ ๋•Œ๋งŒ ๋‘˜ ๋‹ค ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ํ™•์ธ ์‹œํŠธ๊ฐ€ ๋งค์šฐ ๋น ๋ฅด๊ฒŒ ์‚ฌ๋ผ์ง€๊ฑฐ๋‚˜ ์ƒ๋žต๋˜๋Š” ๊ฒฝ์šฐ๋„ ์„ฑ๊ณต์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

inspect

kmsg inspect [--depth <depth>] [--window <window>] [--show-attributes] [--show-path] [--show-frame] [--show-index] [--show-flags] [--show-actions] [--debug-layout] [--row-summary] [--row-range <start:end>]
  • -d, --depth <depth>: ์ตœ๋Œ€ ํƒ์ƒ‰ ๊นŠ์ด (๊ธฐ๋ณธ๊ฐ’: 4)
  • -w, --window <window>: inspect ๋Œ€์ƒ ์ฐฝ ์ธ๋ฑ์Šค (๊ธฐ๋ณธ๊ฐ’: main window)
  • --show-attributes: ๊ฐ ์š”์†Œ์˜ AX attribute ์ถœ๋ ฅ
  • --show-path: ๊ฐ ์š”์†Œ์˜ AX ๊ฒฝ๋กœ ์ถœ๋ ฅ
  • --show-frame: ๊ฐ ์š”์†Œ frame ์ถœ๋ ฅ
  • --show-index: sibling index ์ถœ๋ ฅ
  • --show-flags: ์ƒํƒœ ํ”Œ๋ž˜๊ทธ(enabled/focused/selected/editable) ์ถœ๋ ฅ
  • --show-actions: ์ง€์› AX action ์ถœ๋ ฅ
  • --debug-layout: path/frame/index/flags๋ฅผ ํ•œ ๋ฒˆ์— ์ผœ๋Š” ๋ ˆ์ด์•„์›ƒ ๋””๋ฒ„๊ทธ ๋ฒˆ๋“ค
  • --row-summary: ๋ฉ”์‹œ์ง€ row ์š”์•ฝ ์ถœ๋ ฅ
  • --row-range <start:end>: --row-summary ๊ฒฐ๊ณผ๋ฅผ ํŠน์ • ๋ฒ”์œ„๋งŒ ์ถœ๋ ฅ (inclusive, zero-based)

cache

kmsg help cache
  • status (default): ์บ์‹œ ์ƒํƒœ ์ถœ๋ ฅ
  • clear: ์บ์‹œ ์‚ญ์ œ
  • export <output-path>: ์บ์‹œ JSON ๋‚ด๋ณด๋‚ด๊ธฐ
  • import <input-path>: ์บ์‹œ JSON ๊ฐ€์ ธ์˜ค๊ธฐ
  • warmup [--recipient <recipient>] [--trace-ax] [--keep-window]: send/chats happy-path ๊ฒฝ๋กœ ์บ์‹œ ์›Œ๋ฐ์—…

๊ถŒํ•œ ๋ฌธ์ œ ํ•ด๊ฒฐ

kmsg๋Š” ์†์‰ฌ์šด ์‚ฌ์šฉ(Accessibility) ๊ถŒํ•œ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์•ฑ์ด ์ž๋™ ์š”์ฒญ์— ์‹คํŒจํ•˜๋ฉด:

  1. ์‹œ์Šคํ…œ ์„ค์ • ์—ด๊ธฐ
  2. ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ ๋ฐ ๋ณด์•ˆ > ์†์‰ฌ์šด ์‚ฌ์šฉ
  3. kmsg ํ† ๊ธ€ ์ผœ๊ธฐ

JSON ์ถœ๋ ฅ

read ๋ช…๋ น์€ --json ํ”Œ๋ž˜๊ทธ๋กœ ๊ตฌ์กฐํ™”๋œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

kmsg read "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" --limit 20 --json

์ถœ๋ ฅ ํ˜•์‹

{
  "chat": "ํ™๊ธธ๋™",
  "fetched_at": "2026-02-26T01:23:45.678Z",
  "count": 20,
  "messages": [
    {
      "author": "ํ™๊ธธ๋™",
      "time_raw": "00:27",
      "body": "๋ฐค์ด ๊นŠ์—ˆ๋„ค"
    }
  ]
}

ํ•„๋“œ ์„ค๋ช…

  • chat: ์‹ค์ œ๋กœ ์ฝ์€ ์ฑ„ํŒ…๋ฐฉ ์ œ๋ชฉ
  • fetched_at: ๋ฉ”์‹œ์ง€ ์ˆ˜์ง‘ ์‹œ๊ฐ(ISO-8601 UTC)
  • count: ๋ฐ˜ํ™˜๋œ ๋ฉ”์‹œ์ง€ ๊ฐœ์ˆ˜
  • messages[].author: ์ž‘์„ฑ์ž ์ด๋ฆ„ ((me)๋Š” ๋‚ด ๋ฉ”์‹œ์ง€ ๋˜๋Š” ์ž‘์„ฑ์ž ์ถ”๋ก ์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ)
  • messages[].time_raw: UI์—์„œ ์ฝํžŒ ์‹œ๊ฐ ๋ฌธ์ž์—ด(์—†์œผ๋ฉด null)
  • messages[].body: ๋ฉ”์‹œ์ง€ ๋ณธ๋ฌธ

์ฃผ์˜

  • --json ์‚ฌ์šฉ ์‹œ JSON์€ stdout์œผ๋กœ๋งŒ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.
  • --trace-ax ๋กœ๊ทธ๋Š” stderr๋กœ ๋ถ„๋ฆฌ๋˜๋ฏ€๋กœ OpenClaw ๊ฐ™์€ ํŒŒ์ดํ”„ ์—ฐ๋™์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

watch --json์€ ๋ฐฐ์—ด ํ•˜๋‚˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋Œ€์‹ , ์ƒˆ ๋ฉ”์‹œ์ง€๊ฐ€ ๊ฐ์ง€๋  ๋•Œ๋งˆ๋‹ค pretty JSON ๊ฐ์ฒด ํ•˜๋‚˜์”ฉ์„ ์—ฐ์† ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

kmsg watch "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" --json

์˜ˆ์‹œ ์ด๋ฒคํŠธ:

{
  "chat": "ํ™๊ธธ๋™",
  "detected_at": "2026-03-25T10:20:30.123Z",
  "event": "message",
  "message": {
    "author": "ํ™๊ธธ๋™",
    "time_raw": "10:20",
    "body": "์ƒˆ ๋ฉ”์‹œ์ง€"
  }
}

--include-system์„ ๊ฐ™์ด ์“ฐ๋ฉด event ๊ฐ’์ด system์ธ ๊ฐ์ฒด๋„ ํ•จ๊ป˜ ์ถœ๋ ฅ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

chats --json๋„ ๋™์ผํ•˜๊ฒŒ stdout์—๋งŒ JSON์„ ์ถœ๋ ฅํ•˜๋ฉฐ, ๊ฐ ์ฑ„ํŒ… ํ•ญ๋ชฉ์— chat_id์™€ last_message๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์ฒซ ์‹คํ–‰์ด๋‚˜ ์บ์‹œ self-heal ์‹œ์—๋Š” ๋” ๋А๋ฆด ์ˆ˜ ์žˆ์ง€๋งŒ, ์ดํ›„ ์‹คํ–‰์€ ์ €์žฅ๋œ happy-path AX cache๋ฅผ ์šฐ์„  ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

MCP ์—ฐ๋™

kmsg ๋Š” MCP ๋กœ ๋ถ™์—ฌ์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ์ฒ˜๋Ÿผ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค.

  • MCP: kmsg_read, kmsg_send, kmsg_send_image
  • ์‹ค์‹œ๊ฐ„ ๊ฐ์‹œ: kmsg watch "<chat>" --json

์ฆ‰, OpenClaw ์—ฐ๋™์€ ๋ณดํ†ต ์•„๋ž˜ ๋‘ ํ”„๋กœ์„ธ์Šค๋กœ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

kmsg mcp-server
kmsg watch "์ฑ„ํŒ…๋ฐฉ ์ด๋ฆ„" --json

OpenClaw MCP ์„ค์ • ์˜ˆ์‹œ:

{
  "mcpServers": {
    "kmsg": {
      "command": "kmsg",
      "args": ["mcp-server"],
      "env": {
        "KMSG_DEFAULT_DEEP_RECOVERY": "false",
        "KMSG_TRACE_DEFAULT": "false"
      }
    }
  }
}

์ถ”์ฒœ ์šด์˜ ์ˆœ์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. watch --json ์œผ๋กœ ์ƒˆ ๋ฉ”์‹œ์ง€ ๊ฐ์ง€
  2. OpenClaw ๊ฐ€ ๋‹ต๋ณ€ ์ดˆ์•ˆ ์ƒ์„ฑ
  3. ํ•„์š”ํ•˜๋ฉด ์‚ฌ์šฉ์ž ์Šน์ธ
  4. kmsg_send ๋˜๋Š” kmsg send ๋กœ ์ „์†ก

์ƒ์„ธ ์šด์˜ ๊ฐ€์ด๋“œ๋Š” docs/openclaw.md ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”. ์„ค์ • ํ…œํ”Œ๋ฆฟ์€ docs/openclaw.mcp.example.json ์— ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ์กด tools/kmsg-mcp.py ๋Š” ๋ ˆ๊ฑฐ์‹œ/๊ฐœ๋ฐœ์šฉ ์ฐธ์กฐ ๊ตฌํ˜„์œผ๋กœ ๋‚จ์•„ ์žˆ์ง€๋งŒ, ์„ค์น˜ํ˜• ์‚ฌ์šฉ ํ๋ฆ„์˜ ๊ธฐ๋ณธ ์ง„์ž…์ ์€ kmsg mcp-server ์ž…๋‹ˆ๋‹ค.

๋กœ์ปฌ ๋นŒ๋“œ ๋ฐ ๊ฐœ๋ฐœ

git clone https://github.com/channprj/kmsg.git
cd kmsg
swift build -c release
install -m 755 .build/release/kmsg ~/.local/bin/kmsg

๊ณ ๊ธ‰ ์˜ต์…˜

kmsg send "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" "ํ…Œ์ŠคํŠธ" --trace-ax
kmsg send --chat-id "chat_7f42c5e1d9ab" "ํ…Œ์ŠคํŠธ" --dry-run
kmsg send "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" "ํ…Œ์ŠคํŠธ" --dry-run --trace-ax
kmsg send "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" "ํ…Œ์ŠคํŠธ" --no-cache
kmsg send "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" "ํ…Œ์ŠคํŠธ" --refresh-cache
kmsg chats --json
kmsg send-image "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" "/path/to/image.png" --trace-ax
KMSG_AX_TIMEOUT=0.25 kmsg send "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" "ํ…Œ์ŠคํŠธ"
kmsg inspect --window 0 --depth 20 --debug-layout
kmsg inspect --window 0 --depth 20 --row-summary
kmsg inspect --window 0 --depth 20 --row-summary --row-range 10:35
kmsg cache status
kmsg cache warmup --recipient "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" --trace-ax
kmsg cache warmup --recipient "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" --keep-window
kmsg cache export ./ax-cache.json
kmsg cache import ./ax-cache.json
kmsg read "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" --deep-recovery --trace-ax
kmsg send "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" "ํ…Œ์ŠคํŠธ" --deep-recovery --trace-ax
kmsg send-image "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" "/path/to/image.png" --deep-recovery --trace-ax --keep-window

--deep-recovery๋Š” ๋น ๋ฅธ ์ฐฝ ํƒ์ƒ‰์ด ์‹คํŒจํ•  ๋•Œ๋งŒ relaunch/open ๋ณต๊ตฌ๋ฅผ ์ถ”๊ฐ€๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ์ž๋™์œผ๋กœ ์—ฐ ์นด์นด์˜คํ†ก ์ฐฝ์€ ๋ช…๋ น ์ข…๋ฃŒ ์‹œ ๋‹ซํžˆ๋ฉฐ, --keep-window(๋˜๋Š” -k)๋กœ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋””๋ฒ„๊น… ๊ฐ€์ด๋“œ (inspect / trace-ax)

๋ฉ”์‹œ์ง€ ์ฝ๊ธฐ/๋ณด๋‚ด๊ธฐ๊ฐ€ ๊ธฐ๋Œ€์™€ ๋‹ค๋ฅด๋ฉด ์•„๋ž˜ ์ˆœ์„œ๋กœ ์ƒํƒœ๋ฅผ ์ˆ˜์ง‘ํ•ด ์ฃผ์„ธ์š”.

# 1) ๋Œ€์ƒ ์ฑ„ํŒ…์ฐฝ ๊ตฌ์กฐ ํ™•์ธ
kmsg inspect --window 0 --depth 20 --debug-layout

# 1-1) ๋ฉ”์‹œ์ง€ row ํŒŒ์‹ฑ ์ง„๋‹จ
kmsg inspect --window 0 --depth 20 --row-summary
kmsg inspect --window 0 --depth 20 --row-summary --row-range 10:30

# 2) ์ฝ๊ธฐ ๊ฒฝ๋กœ/AX ๋กœ๊ทธ ํ™•์ธ
kmsg read "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" --limit 20 --trace-ax

# 3) ๋ณด๋‚ด๊ธฐ ๊ฒฝ๋กœ/AX ๋กœ๊ทธ ํ™•์ธ
kmsg send "๋ณธ์ธ, ์นœ๊ตฌ, ๋˜๋Š” ๋‹จํ†ก๋ฐฉ ์ด๋ฆ„" "ํ…Œ์ŠคํŠธ" --trace-ax --dry-run
  • AXTextArea, value: "..." ๋Š” ์‹ค์ œ ๋ฉ”์‹œ์ง€ ๋ณธ๋ฌธ ํ›„๋ณด์ž…๋‹ˆ๋‹ค.
  • AXStaticText, value: "5\n00:27" ๊ฐ™์€ ๊ฐ’์€ ๋ณดํ†ต ์นด์šดํŠธ/์‹œ๊ฐ„ ๋ฉ”ํƒ€ ์ •๋ณด์ž…๋‹ˆ๋‹ค.
  • --debug-layout์„ ์ผœ๋ฉด path/frame/index/flags๊ฐ€ ํ•จ๊ป˜ ์ถœ๋ ฅ๋˜์–ด ์œ„์น˜ ๊ธฐ๋ฐ˜ ๋ถ„์„์ด ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.
  • --row-summary๋Š” read ํŒŒ์„œ ๊ธฐ์ค€์œผ๋กœ row๋ณ„ authorCandidates, time, buttonTitles๋ฅผ ๋น ๋ฅด๊ฒŒ ์ ๊ฒ€ํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์ด์Šˆ ๋ณด๊ณ  ์‹œ inspect ์ถœ๋ ฅ๊ณผ --trace-ax ์ถœ๋ ฅ์„ ํ•จ๊ป˜ ์ฒจ๋ถ€ํ•˜๋ฉด ์›์ธ ํŒŒ์•…์ด ๋นจ๋ผ์ง‘๋‹ˆ๋‹ค.

Coding Agent์—๊ฒŒ ์š”์ฒญํ•˜๊ธฐ

๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๊ฑฐ๋‚˜ ๋ฒ„๊ทธ ์ˆ˜์ •์„ ์›ํ•  ๋•Œ Coding Agent์—๊ฒŒ ์•„๋ž˜ ์ •๋ณด์™€ ํ•จ๊ป˜ ์š”์ฒญํ•˜๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

  1. ์‹คํ–‰ํ•œ ๋ช…๋ น์–ด: kmsg read ... --trace-ax, kmsg inspect ...
  2. ๊ธฐ๋Œ€ ๊ฒฐ๊ณผ: ๋ฌด์—‡์ด ๋ณด์—ฌ์•ผ ํ•˜๋Š”์ง€
  3. ์‹ค์ œ ๊ฒฐ๊ณผ: ํ˜„์žฌ ๋ฌด์—‡์ด ์ถœ๋ ฅ๋˜๋Š”์ง€
  4. ๊ด€๋ จ ๋กœ๊ทธ: inspect ๋ณธ๋ฌธ ๊ตฌ๊ฐ„ (AXRow > AXCell > AXTextArea) + trace-ax
kmsg read๊ฐ€ ๋ฉ”์‹œ์ง€ ๋ณธ๋ฌธ ๋Œ€์‹  ์‹œ๊ฐ„/์ˆซ์ž๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
inspect ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ์ค€์œผ๋กœ AXRow > AXCell > AXTextArea.value๋ฅผ ์šฐ์„  ์ถ”์ถœํ•˜๋„๋ก ์ˆ˜์ •ํ•ด ์ฃผ์„ธ์š”.
README ๋””๋ฒ„๊น… ๊ฐ€์ด๋“œ๋„ ํ•จ๊ป˜ ์—…๋ฐ์ดํŠธํ•ด ์ฃผ์„ธ์š”.

Deploy

v* ํƒœ๊ทธ๋ฅผ ํ‘ธ์‹œํ•˜๋ฉด GitHub Actions๊ฐ€ ์ž๋™์œผ๋กœ ๋นŒ๋“œํ•ด์„œ kmsg-macos-universal ํŒŒ์ผ์„ Releases์— ์—…๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ workflow๊ฐ€ channprj/homebrew-tap ๋ฆฌ๋ชจํŠธ๊นŒ์ง€ ์ž๋™์œผ๋กœ ๋™๊ธฐํ™”ํ•˜๋ฉฐ, TAP_REPO_TOKEN์ด ์—†๊ฑฐ๋‚˜ tap push๊ฐ€ ์‹คํŒจํ•˜๋ฉด ๋ฆด๋ฆฌ์ฆˆ๋„ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

๋ฐฐํฌ ์ „์— VERSION ํŒŒ์ผ ๊ฐ’์„ ๋จผ์ € ์—…๋ฐ์ดํŠธํ•˜์„ธ์š”. ๋ฒ„์ „ ๊ทœ์น™์€ VERSIONING.md๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

# gh ํ† ํฐ์ด ๋งŒ๋ฃŒ๋์œผ๋ฉด ์žฌ๋กœ๊ทธ์ธ
gh auth login -h github.com

# ๋ฐฐํฌ ํƒœ๊ทธ ์ƒ์„ฑ/ํ‘ธ์‹œ
git tag v2026.0422.22
git push origin v2026.0422.22

ํ•„์š”ํ•˜๋ฉด Actions๋ฅผ ์ˆ˜๋™ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. workflow_dispatch์—์„œ tag๋ฅผ ๋น„์›Œ๋‘๋ฉด VERSION ํŒŒ์ผ์„ ์ฝ์–ด v<version>์œผ๋กœ ์ž๋™ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. tag๋ฅผ ์ง์ ‘ ์ž…๋ ฅํ•  ๊ฒฝ์šฐ vYYYY.MMDD.COUNT ํ˜•์‹๋งŒ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค.

# ํƒœ๊ทธ๋ฅผ ์ง์ ‘ ์ง€์ •ํ•ด์„œ ์‹คํ–‰
gh workflow run release.yml -f tag=v2026.0422.22

# tag ๋ฏธ์ง€์ • ์‹œ VERSION(์˜ˆ: 2026.0422.22) ๊ธฐ๋ฐ˜์œผ๋กœ ์‹คํ–‰
gh workflow run release.yml

Homebrew ์ž๋™ ๋™๊ธฐํ™” ์„ค์ • (์ตœ์ดˆ 1ํšŒ)

  1. channprj/homebrew-tap ์ €์žฅ์†Œ๋ฅผ ๋งŒ๋“ค๊ณ  Formula/ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ค€๋น„ํ•ฉ๋‹ˆ๋‹ค.
  2. kmsg ์ €์žฅ์†Œ Secrets์— TAP_REPO_TOKEN์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    • ๊ถŒํ•œ: homebrew-tap ์ €์žฅ์†Œ contents: write
  3. tap ์ €์žฅ์†Œ ๊ธฐ๋ณธ ๋ธŒ๋žœ์น˜๊ฐ€ main์ด ์•„๋‹ˆ๋ฉด .github/workflows/release.yml์˜ TAP_REPO_REF ๊ฐ’์„ ๋งž์ถฐ ์ฃผ์„ธ์š”.

์ด์ œ TAP_REPO_TOKEN์€ ์„ ํƒ์ด ์•„๋‹ˆ๋ผ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค. secret์ด ์—†์œผ๋ฉด ๋ฐ”์ด๋„ˆ๋ฆฌ release๋งŒ ๋งŒ๋“ค๊ณ  ๋๋‚ด์ง€ ์•Š๊ณ , workflow ์ „์ฒด๊ฐ€ ์‹คํŒจํ•ด์„œ tap ๋ฐ˜์˜ ๋ˆ„๋ฝ์„ ๋ฐ”๋กœ ์žก๋„๋ก ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

๋ฆด๋ฆฌ์ฆˆ ํ›„ ์‚ฌ์šฉ์ž๋Š” ์•„๋ž˜ ๋ช…๋ น์œผ๋กœ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

brew install channprj/tap/kmsg
brew install channprj/tap/kmsg@2026.0422.22

kmsg formula๋Š” ํ•ญ์ƒ ์ตœ์‹  ๋ฆด๋ฆฌ์ฆˆ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ , kmsg@YYYY.MMDD.COUNT formula๋Š” ์ตœ๊ทผ 10๊ฐœ exact release๋งŒ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ๋‹ค๋ฅธ ๋ฒ„์ „์„ ์„ค์น˜ํ–ˆ๋‹ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ๋งํฌ๋ฅผ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

brew unlink kmsg
brew link --overwrite kmsg@2026.0422.22

๊ธฐํƒ€

์™œ Swift ๋กœ ๋งŒ๋“ค์—ˆ๋‚˜์š”?

kmsg ๋Š” macOS ์ „์šฉ ๋„๊ตฌ์ด๊ณ , ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ด AppKit, ApplicationServices, AXUIElement, CGEvent ๊ฐ™์€ macOS native API ์œ„์— ์˜ฌ๋ผ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. Swift ๋Š” ์ด API ๋“ค๊ณผ์˜ ์—ฐ๊ฒฐ์ด ๊ฐ€์žฅ ์ง์ ‘์ ์ด๊ณ  ์•ˆ์ •์ ์ด๋ฉฐ, Objective-C ๊ณ„์—ด ํ”„๋ ˆ์ž„์›Œํฌ์™€์˜ ์ƒํ˜ธ์šด์šฉ๋„ ์ž์—ฐ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ Python ์€ ๋Ÿฐํƒ€์ž„/๋ฐฐํฌ ์˜์กด์„ฑ์ด ๋Š˜์–ด๋‚˜๊ณ , Rust ๋Š” macOS framework binding ๊ณผ FFI ๊ด€๋ฆฌ ๋ณต์žก๋„๊ฐ€ ์ปค์ง€๊ธฐ ๋•Œ๋ฌธ์— ํ˜„์žฌ ๋ชฉ์ ์—๋Š” Swift ์ชฝ์ด ๊ตฌํ˜„๊ณผ ์œ ์ง€๋ณด์ˆ˜ ๋ชจ๋‘ ๋” ์‹ค์šฉ์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

AX ๊ฐ€ ๋ญ”๊ฐ€์š”?

AX ๋Š” macOS Accessibility(์†์‰ฌ์šด ์‚ฌ์šฉ) API ๋ฅผ ๋œปํ•ฉ๋‹ˆ๋‹ค. ์•ฑ์˜ ์ฐฝ, ๋ฒ„ํŠผ, ์ž…๋ ฅ์ฐฝ, ํ…์ŠคํŠธ ๊ฐ™์€ UI ์š”์†Œ๋ฅผ AXUIElement ๋กœ ์ฝ๊ณ  ์ œ์–ดํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์‹œ์Šคํ…œ ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค.

์™œ AX ๋ฅผ ์‚ฌ์šฉํ•˜๋‚˜์š”?

KakaoTalk ์€ ๊ณต์‹์ ์œผ๋กœ CLI ๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์‹ค์ œ ์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๊ณ  ํด๋ฆญํ•˜๋Š” UI ๋ฅผ ์‹œ์Šคํ…œ ๋ ˆ๋ฒจ์—์„œ ์ฝ๊ณ  ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ํ˜„์‹ค์ ์ธ ๋ฐฉ๋ฒ•์ด AX ์ž…๋‹ˆ๋‹ค. kmsg ์˜ ์ฑ„ํŒ… ๋ชฉ๋ก ์ฝ๊ธฐ, ๊ฒ€์ƒ‰, ๋ฉ”์‹œ์ง€ ์ „์†ก, ์ด๋ฏธ์ง€ ์ „์†ก, ์ฐฝ ์ œ์–ด๊ฐ€ ๋ชจ๋‘ ์ด ๋ ˆ์ด์–ด ์œ„์—์„œ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— Accessibility permission ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์™œ KakaoTalk ์˜ LOCO Protocol ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋‚˜์š”?

LOCO Protocol ์„ ์“ฐ๋ ค๋ฉด ์‚ฌ์‹ค์ƒ ๋น„๊ณต๊ฐœ ๋™์ž‘์„ ๋ฆฌ๋ฒ„์Šค ์—”์ง€๋‹ˆ์–ด๋งํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ €๋Š” ์ด ์ ‘๊ทผ์ด ๋ช…๋ฐฑํ•œ ์•ฝ๊ด€ ์œ„๋ฐ˜์œผ๋กœ ํ•ด์„๋  ์—ฌ์ง€๊ฐ€ ํฌ๋‹ค๊ณ  ๋ด…๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์ด๋Ÿฐ ๋ฐฉ์‹์˜ ์ž๋™ํ™”๋‚˜ ๋น„๊ณต์‹ ํ”„๋กœํ† ์ฝœ ์‚ฌ์šฉ๊ณผ ๊ด€๋ จํ•ด ๊ณ„์ •์ด ์˜๊ตฌ์ •์ง€๋œ ์‚ฌ๋ก€๋„ ์ด๋ฏธ ์ ์ง€ ์•Š๊ฒŒ ์•Œ๋ ค์ ธ ์žˆ์–ด์„œ, ๊ธฐ๋Šฅ์ ์œผ๋กœ ๊ฐ€๋Šฅํ•˜๋”๋ผ๋„ ๊ฐ์ˆ˜ํ•ด์•ผ ํ•˜๋Š” ๋ฆฌ์Šคํฌ๊ฐ€ ๋„ˆ๋ฌด ํฌ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด AX ๋ฐฉ์‹์€ ์‚ฌ์šฉ์ž๊ฐ€ ์‹ค์ œ๋กœ ๋ณด๊ณ  ์กฐ์ž‘ํ•˜๋Š” KakaoTalk UI ๋ฅผ macOS ์˜ ๊ณต์‹ Accessibility API ๋กœ ์ฝ๊ณ  ์ œ์–ดํ•˜๋Š” ์ ‘๊ทผ์ด๋ผ, ์ ์–ด๋„ LOCO ๋ฅผ ์ง์ ‘ ๊ฑด๋“œ๋ฆฌ๋Š” ๋ฐฉ์‹๋ณด๋‹ค๋Š” ์ƒ๋Œ€์ ์œผ๋กœ ์•ˆ์ „ํ•  ๊ฒƒ์ด๋ผ๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์ด๊ฒƒ๋„ ์–ด๋””๊นŒ์ง€๋‚˜ ์ œ ๊ฐœ์ธ์ ์ธ ํŒ๋‹จ์ผ ๋ฟ์ด๋ฉฐ, ์นด์นด์˜ค์˜ ๊ณต์‹ ์ž…์žฅ์ด๋‚˜ ์นด์นด์˜ค ๋ฒ•๋ฌดํŒ€์˜ ํ•ด์„๊ณผ๋Š” ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ์•ฝ๊ด€ ํ•ด์„๊ณผ ์ œ์žฌ ๊ธฐ์ค€์€ ์นด์นด์˜ค ์ธก ํŒ๋‹จ์ด ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ธฐ๋Šฅ์„ Rust ๋กœ ์ž‘์„ฑํ•˜๋ฉด ๋” ๋นจ๋ผ์งˆ๊นŒ์š”?

์กฐ๊ธˆ์€ ๋นจ๋ผ์งˆ ์ˆ˜ ์žˆ์ง€๋งŒ, ์ฒด๊ฐ ์„ฑ๋Šฅ ํ–ฅ์ƒ์€ ์ œํ•œ์ ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ํฝ๋‹ˆ๋‹ค. ์ด CLI ์˜ ์ฃผ๋œ ์ง€์—ฐ์€ ์–ธ์–ด ๋Ÿฐํƒ€์ž„๋ณด๋‹ค AXUIElement ํ˜ธ์ถœ, UI ํƒ์ƒ‰, KakaoTalk ์ฐฝ ํ™œ์„ฑํ™”/๋ณต๊ตฌ, ์‹ค์ œ ์•ฑ ์‘๋‹ต ์‹œ๊ฐ„์—์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. Rust ๋กœ ๋ฐ”๊พธ๋ฉด cold start ๋‚˜ ์ˆœ์ˆ˜ stdio/JSON ์ฒ˜๋ฆฌ ๊ฐ™์€ ๋ถ€๋ถ„์€ ์•ฝ๊ฐ„ ์œ ๋ฆฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ „์ฒด end-to-end latency ๋Š” ํฌ๊ฒŒ ๋‹ฌ๋ผ์ง€์ง€ ์•Š์„ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  macOS native framework binding ๊ณผ ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ์€ Swift ๋ณด๋‹ค ๋†’์•„์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Inspiration

This project is strongly inspired by steipete and his works.

References

About

CLI for KakaoTalk.app so your agent can send and receive k-messages.

Topics

Resources

License

Stars

Watchers

Forks

Contributors