커맨드 시스템

/something은 모두 커맨드 — 3가지 실행 전략, 등록 파이프라인, 가용성 게이팅.

01

개요

사용자가 /로 시작하는 입력을 하면 커맨드 시스템이 활성화됩니다. Claude Code의 커맨드 시스템은 단순한 디스패치 테이블이 아닙니다 — 3가지 실행 전략, 동적 등록, 가용성 게이팅, 캐시 관리를 포함하는 완전한 프레임워크입니다.

다루는 소스 파일: commands/registry.tscommands/base.tscommands/executor.tscommands/availability.ts

커맨드 시스템의 핵심 구성요소:

02

3가지 커맨드 타입

local 커맨드

local 커맨드는 순수 TypeScript 함수로 실행됩니다. REPL 상태를 직접 변경하거나, 설정을 수정하거나, 즉각적인 부수 효과를 발생시킵니다. AI 모델 호출이 필요 없는 작업에 사용됩니다.

// local 커맨드: 즉시 실행, 모델 호출 없음
const clearCommand: LocalCommand = {
  type: 'local',
  name: 'clear',
  description: '대화 기록을 초기화합니다',
  execute(context) {
    context.clearConversation()
    return { success: true }
  }
}

local-jsx 커맨드

local-jsx 커맨드는 React 컴포넌트를 반환합니다. 대화상자, 설정 화면, 인터랙티브 위자드 등 UI가 필요한 커맨드에 사용됩니다. 반환된 JSX는 Ink 렌더링 엔진을 통해 터미널에 표시됩니다.

// local-jsx 커맨드: React 컴포넌트 반환
const settingsCommand: LocalJsxCommand = {
  type: 'local-jsx',
  name: 'settings',
  description: '설정 화면을 표시합니다',
  render(context) {
    return <SettingsDialog onClose={context.dismiss} />
  }
}

prompt 커맨드

prompt 커맨드는 사용자 입력을 AI 모델에 전달하기 전에 프롬프트를 변환합니다. 사용자가 입력한 내용에 시스템 프롬프트를 추가하거나, 특정 컨텍스트를 주입하거나, 모델의 동작을 조정합니다.

// prompt 커맨드: 사용자 입력을 프롬프트로 변환
const reviewCommand: PromptCommand = {
  type: 'prompt',
  name: 'review',
  description: '코드 리뷰를 수행합니다',
  buildPrompt(userInput, context) {
    return `다음 코드를 리뷰해주세요:\n${context.selectedCode}\n\n사용자 요청: ${userInput}`
  }
}
03

CommandBase 계약 (11개 필드)

모든 커맨드는 CommandBase 인터페이스를 구현해야 합니다. 이 계약은 커맨드의 정체성, 실행 방식, 가용성 조건을 정의합니다.

interface CommandBase {
  name: string              // 고유 식별자 (/name으로 호출)
  type: CommandType          // 'local' | 'local-jsx' | 'prompt'
  description: string       // 사용자에게 표시되는 설명
  aliases: string[]          // 대체 이름 목록
  isAvailable: AvailFn       // 현재 컨텍스트에서 사용 가능 여부
  isHidden: boolean          // 자동완성에서 숨길지 여부
  argDescription: string    // 인수 설명
  userFacingName: string    // UI에 표시되는 이름
  category: string          // 그룹화 카테고리
  requiredFeatures: Feature[] // 필요한 기능 플래그
  priority: number          // 자동완성 정렬 우선순위
}
딥 다이브 — 가용성과 기능 게이팅의 차이

isAvailable은 런타임 조건을 체크합니다 — 예를 들어 현재 대화에 코드가 선택되어 있는지, 특정 모드가 활성화되어 있는지. requiredFeatures는 빌드 타임 기능 플래그입니다 — 특정 기능이 빌드에 포함되지 않으면 커맨드 자체가 등록되지 않습니다. 이 이중 게이팅은 커맨드가 사용 불가능할 때 명확한 이유를 제공합니다.

04

등록 파이프라인: 정적 → 동적 → 필터

flowchart LR A["정적 등록"] --> B["동적 등록"] B --> C["필터 적용"] C --> D["최종 커맨드 목록"] style A fill:#c47a50,color:#1a1816 style D fill:#6e9468,color:#1a1816

정적 등록

빌드 타임에 알려진 커맨드들이 정적으로 등록됩니다. commands/ 디렉토리의 각 파일이 하나의 커맨드를 export하며, 레지스트리에 자동으로 수집됩니다.

동적 등록

런타임에 추가되는 커맨드입니다. 프로젝트의 .claude/commands/ 디렉토리에서 커스텀 커맨드를 로드하거나, MCP 서버에서 제공하는 커맨드를 등록합니다. 동적 커맨드는 정적 커맨드와 동일한 CommandBase 계약을 따릅니다.

필터 적용

등록된 모든 커맨드에 대해 isAvailable()requiredFeatures를 체크하여 현재 컨텍스트에서 사용 가능한 커맨드만 남깁니다.

// 등록 파이프라인 흐름
function getAvailableCommands(context: CommandContext): Command[] {
  const staticCmds = getStaticCommands()
  const dynamicCmds = getDynamicCommands(context.projectRoot)
  const allCmds = [...staticCmds, ...dynamicCmds]

  return allCmds
    .filter(cmd => cmd.requiredFeatures.every(f => feature(f)))
    .filter(cmd => cmd.isAvailable(context))
    .sort((a, b) => b.priority - a.priority)
}
05

입력 처리와 REPL 통합

사용자가 /를 입력하면 REPL은 일반 텍스트 모드에서 커맨드 모드로 전환됩니다. 자동완성이 활성화되고, 사용 가능한 커맨드 목록이 표시됩니다.

// REPL 입력 처리 — 커맨드 디스패치
function handleInput(input: string): void {
  if (input.startsWith('/')) {
    const [name, ...args] = input.slice(1).split(' ')
    const cmd = registry.resolve(name) // 이름 + 별칭으로 조회
    if (!cmd) { showUnknownCommand(name); return }

    switch (cmd.type) {
      case 'local':     cmd.execute(context);          break
      case 'local-jsx': mountDialog(cmd.render(context)); break
      case 'prompt':    sendToModel(cmd.buildPrompt(args, context)); break
    }
  }
}

캐시 관리

동적 커맨드의 로드 결과는 캐시됩니다. 프로젝트 루트가 변경되거나 .claude/commands/ 디렉토리의 파일이 변경되면 캐시가 무효화됩니다. 이를 통해 매 입력마다 파일시스템을 읽지 않으면서도 최신 커맨드 목록을 유지합니다.

브릿지와 원격 모드

브릿지 모드에서는 커맨드가 원격 프로세스로 전달됩니다. locallocal-jsx 커맨드는 로컬에서 실행되지만, prompt 커맨드는 원격 세션의 모델에 전달됩니다. 원격 모드에서는 일부 로컬 커맨드가 비활성화됩니다.

주의사항

동적 커맨드의 이름이 정적 커맨드와 충돌하면 정적 커맨드가 우선합니다. 사용자는 이름 충돌을 인지하지 못할 수 있으므로, 커스텀 커맨드에는 프로젝트별 접두사를 사용하는 것이 좋습니다.

06

핵심 요약

핵심 포인트

  • 커맨드는 3가지 타입으로 나뉩니다: local(즉시 실행), local-jsx(React UI 반환), prompt(모델 프롬프트 변환)
  • CommandBase는 11개 필드의 계약으로, 모든 커맨드가 일관된 인터페이스를 제공합니다
  • 등록 파이프라인은 정적 → 동적 → 필터의 3단계로, 빌드 타임과 런타임 커맨드를 모두 지원합니다
  • 가용성 게이팅(isAvailable)과 기능 게이팅(requiredFeatures)이 이중으로 커맨드 접근을 제어합니다
  • 동적 커맨드 캐시는 프로젝트 루트 변경이나 파일 변경 시 무효화됩니다
  • 브릿지 모드에서 prompt 커맨드는 원격으로, local/local-jsx는 로컬에서 실행됩니다
07

지식 확인

퀴즈 — 5문제

Q1. local-jsx 커맨드가 local 커맨드와 다른 점은 무엇인가요?

  • A) AI 모델을 호출하여 결과를 생성한다
  • B) 비동기적으로 실행되어 더 느리다
  • C) React 컴포넌트(JSX)를 반환하여 Ink 렌더러를 통해 UI를 표시한다
  • D) 파일 시스템을 직접 수정할 수 있다
local-jsx 커맨드는 render() 메서드를 통해 React 컴포넌트를 반환합니다. 반환된 JSX는 Ink 렌더링 엔진을 통해 터미널에 대화상자나 설정 화면으로 표시됩니다. local 커맨드는 UI 없이 즉시 실행됩니다.

Q2. 동적 커맨드와 정적 커맨드의 이름이 충돌하면 어떻게 되나요?

  • A) 동적 커맨드가 우선한다
  • B) 정적 커맨드가 우선한다
  • C) 에러가 발생하고 두 커맨드 모두 비활성화된다
  • D) 사용자에게 선택을 요청한다
정적 커맨드는 빌드 타임에 등록되어 안정적인 동작을 보장합니다. 동적 커맨드와 이름이 충돌하면 정적 커맨드가 우선하여 핵심 기능이 오버라이드되는 것을 방지합니다.

Q3. prompt 커맨드의 buildPrompt()는 무엇을 하나요?

  • A) 사용자 입력과 컨텍스트를 결합하여 AI 모델에 보낼 프롬프트를 생성한다
  • B) 사용자에게 추가 입력을 요청하는 대화상자를 표시한다
  • C) 커맨드의 도움말 텍스트를 생성한다
  • D) 이전 대화 기록을 요약한다
buildPrompt()는 사용자가 입력한 인수와 현재 컨텍스트(선택된 코드, 파일 경로 등)를 결합하여 AI 모델에 전송할 완성된 프롬프트를 생성합니다. 이를 통해 특정 작업에 최적화된 프롬프트를 자동으로 구성할 수 있습니다.

Q4. requiredFeaturesisAvailable의 차이는 무엇인가요?

  • A) 둘 다 런타임에 평가되는 동일한 메커니즘이다
  • B) requiredFeatures는 성능 최적화, isAvailable은 보안 검사용이다
  • C) requiredFeatures는 사용자 권한, isAvailable은 시스템 요구사항을 검사한다
  • D) requiredFeatures는 빌드 타임 기능 플래그로 커맨드 등록 여부를 결정하고, isAvailable은 런타임 조건으로 사용 가능 여부를 결정한다
requiredFeatures는 빌드 타임에 특정 기능이 포함되어 있는지 확인합니다. 기능이 빌드에서 제외되면 커맨드 자체가 등록되지 않습니다. isAvailable은 런타임에 현재 대화 상태, 모드, 선택 영역 등을 체크하여 커맨드의 일시적 사용 가능 여부를 결정합니다.

Q5. 브릿지 모드에서 커맨드 실행은 어떻게 달라지나요?

  • A) 모든 커맨드가 원격으로 실행된다
  • B) 모든 커맨드가 로컬에서 실행된다
  • C) local/local-jsx는 로컬에서, prompt는 원격 세션의 모델에서 실행된다
  • D) 커맨드 시스템이 완전히 비활성화된다
브릿지 모드에서 locallocal-jsx 커맨드는 UI 관련이므로 로컬에서 실행됩니다. prompt 커맨드는 AI 모델 호출이 필요하므로 원격 세션으로 전달됩니다. 이 분리를 통해 로컬 UI 응답성을 유지하면서 원격 모델과 통신합니다.
0 / 5