본문 바로가기
C.W.K.
Stream
Lesson 04 of 05 · published

터미널은 일급 타깃이야

~11 min · terminal-profile, coding-agents, safety, bracketed-paste

Level 0불씨 없음
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"텍스트 필드에선 잘못된 붙여넣기가 창피해. 터미널에선 잘못된 붙여넣기가 실행돼."

왜 터미널이 특별한가

Terminal, iTerm, VS Code 나 Cursor 안의 터미널, 아니면 Codex 나 Claude Code 같은 코딩 에이전트 CLI 에 받아쓰는 건 일급 사용 사례야. 아빠는 하루 종일 거기서 사니까. 그런데 터미널은 범용 텍스트 필드가 아니야. 거기 붙여넣은 텍스트는 실행될 수 있어. 돌기 전에 읽을 수도 없는 큰 붙여넣기 블록은 잘해야 사람을 방해하고, 못하면 위험해. 그래서 Firekeeper 는 터미널한테 Mail 작성 창이랑은 다른 규칙의 삽입 프로파일을 따로 줘.

터미널 프로파일 규칙

번들 id 로 타깃이 터미널인지 알아내. Terminal, iTerm2, Ghostty, 에디터 안에 붙은 터미널, 에이전트 CLI 까지. 그리고 동작을 바꿔.

  • 짧은 한 줄짜리 받아쓰기. 평범하게 붙여넣어.
  • 길거나 여러 줄짜리 받아쓰기. 보이고 손댈 수 있게 유지해. 명시적으로 '터미널로 보내기' 를 누르는 프리뷰 오버레이를 띄우거나, 천천히 줄 단위로 넣거나, 타깃이 지원하면 bracketed paste 를 인식해서 넣거나.
  • 긴 터미널 받아쓰기 뒤에 Enter 를 절대 자동으로 안 눌러. 유저가 명시적으로 '엔터 쳐' 라고 말하고 거기에 그 동작을 켜 뒀을 때만 눌러.

여기서 전부를 떠받치는 건 Enter 규칙이야. 자동으로 실행되는 받아쓰기는 곧 생각이 덜 끝난 명령도 돌려 버릴 수 있는 받아쓰기니까.

범용 텍스트 필드:  텍스트 붙여넣기 -> 완료
터미널 프로파일:    터미널 감지
                     -> 짧아? 붙여넣기
                     -> 길어? 프리뷰 / 청크 / bracketed-paste, 편집 가능 유지
                     -> 말한 '엔터 쳐' 그리고 켜졌을 때만 Enter

작업 루프 존중

더 깊은 생각은 유저가 실제로 일하는 흐름을 존중하는 거야. 코딩 에이전트한테 긴 프롬프트를 받아쓰는 개발자는 보내기 전에 보고 고치고 싶어 해. 도구가 그 흐름이랑 싸우지 않고 맞춰 준다는 게 그런 거고. Wispr Flow 도 이걸 알아냈어. Firekeeper 는 여기서 한 발 더 가서, 있으면 좋은 기능이 아니라 설계 요구사항으로 다뤄. 터미널 프로파일이 '받아쓸 수 있어' 를 '내가 실제로 개발하는 환경에 안전하게 받아쓸 수 있어' 로 바꿔 주는 거지.

앱이 자기 판단으로 터미널에 Enter 를 합성하는 일은 절대 없어야 해. 터미널 받아쓰기 뒤에 Enter 를 자동으로 누르면 반쯤 만든 명령이 돌아갈 수 있어. 합성된 Enter 로 가는 길은 하나뿐이야. 유저가 설정을 켜 둔 채로 '엔터 쳐' 라고 명시적으로 말하는 것. 의도적인 신호 두 개고, 추론은 절대 아니고. 애매하면 커서를 텍스트 끝에 두고 사람이 누르게 둬.
번들 id 로 타깃 종류를 알아내고, 그다음에 특화해. 최전면 앱의 번들 식별자는 삽입 프로파일을 고르는 데 쓸 수 있는 안정적이고 싼 신호야. 알려진 터미널이랑 코딩 에이전트 번들 id 목록을 유지하다가, 타깃이 거기 걸리면 조심스러운 프로파일로 바꿔. 모르는 앱은 터미널 프로파일이 아니라 안전한 범용 경로를 받고.

Code

터미널은 조심스러운 프로파일을 받고, Enter 는 절대 추론 안 함·swift
let terminalBundles: Set<String> = [
    "com.apple.Terminal", "com.googlecode.iterm2", "com.mitchellh.ghostty",
    "com.microsoft.VSCode", "com.todesktop.230313mzl4w4u92" /* Cursor */,
    // ...감지 가능한 코딩-에이전트 CLI 들도
]

func insertInto(_ t: TargetSnapshot, text: String, spokeEnter: Bool, enterEnabled: Bool) async {
    if terminalBundles.contains(t.appBundleID) {
        if text.isMultiline { await previewThenSend(text, into: t) }  // 보이고/편집 가능 유지
        else { await pasteInsert(text, into: t) }
        // Enter 는 명시적 말한 명령 그리고 설정 켜짐 둘 다 필요.
        if spokeEnter && enterEnabled { await synthesizeReturn() }
    } else {
        await insertAtomic(text, into: t)   // 범용 안전 경로
    }
}

External links

Exercise

텍스트를 넣었을 때 평범한 텍스트 필드랑 결과가 달라지는 타깃 셋을 적어봐. 터미널, 스프레드시트 수식 입력줄, Enter 로 보내는 채팅 입력 같은 거. 각각에 대해 위험한 기본 동작이 뭔지, 그리고 거기 받아쓰기를 안전하게 하려면 어떤 특화 규칙을 더할지 써봐.
Hint
패턴은 같아. 그 타깃이 텍스트를 담는 것 말고 뭘 더 하는지 찾아내는 거야. 실행하거나(터미널), 계산하거나(수식 입력줄), 보내거나(Enter 로 전송되는 채팅). 특화 규칙은 늘 제일 되돌릴 수 없는 결과를 묶어. 자동 실행 금지, 자동 계산 금지, 자동 전송 금지. 타깃 종류를 알아내고, 위험한 마지막 단계는 사람이 할 때까지 붙들어 둬.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.