Free

Slash Commands에서 Skills로: 언제, 어떻게 마이그레이션할 것인가

custom commands가 Skills에 통합됐다. 언제 `.claude/skills/`로 옮겨야 하고, 옮기면 무엇을 더 할 수 있는지


앞선 세 편에서 slash command 의 전체 사용법을 훑었다. Markdown 파일에서 시작해서, !command 주입, 그리고 subagent 와 MCP 오케스트레이션까지. 이 내용들은 여전히 유효하다. 다만 공식에서 뒤에서 조용히 한 가지를 합쳐 버렸다: custom commands 가 Skills 에 통합됐다.

.claude/commands/review.md 는 여전히 돌아가고, /review 도 그대로 쓸 수 있다. 고칠 건 아무것도 없다. 하지만 command 에 더 많은 능력을 주고 싶다면——여러 파일, fork 실행, 경로 기반 자동 활성화——새로운 위치는 .claude/skills/review/SKILL.md 다. 이 글은 두 가지를 정리한다: 어떻게 옮기는지, 그리고 옮길 가치가 있는지.


최소 마이그레이션: 경로 하나만 바꾸고 나머지는 그대로

공식 호환성 약속은 명확하다. .claude/commands/deploy.md.claude/skills/deploy/SKILL.md 는 둘 다 /deploy 로 등록되고 동작이 완전히 같다. 이름이 충돌하면 skills 가 우선이다.

최소 단계:

cd .claude/commands
mkdir -p ../skills/review
mv review.md ../skills/review/SKILL.md

파일 내용은 한 줄도 건드릴 필요가 없다. YAML frontmatter, !`command`, @file, $ARGUMENTS—전부 호환된다.

하지만 여기서 끝나면 "그럼 왜 옮겼어?" 라는 질문이 바로 나온다. 답은 skill 디렉터리에는 여러 파일을 둘 수 있지만, command 는 단일 파일로 고정된다는 점이다.


왜 마이그레이션해야 하는가: SKILL.md 가 더 주는 네 가지

1. 보조 파일: 긴 문서를 밖으로 빼내기

SKILL.md 는 엔트리포인트고, 같은 디렉터리에는 임의의 보조 파일을 둘 수 있다. 이런 구성을 떠올려 보자:

.claude/skills/review/
├── SKILL.md            # 엔트리. 짧은 지시 + 참조
├── checklist.md        # 긴 코드 리뷰 체크리스트
├── examples/
│   └── good-diff.md    # 좋은 예시
└── scripts/
    └── lint.sh         # SKILL.md 에서 호출 가능한 스크립트

SKILL.md 에서 상대 경로로 참조한다:

현재 diff 를 리뷰하고, [checklist.md](checklist.md) 의 기준을 따를 것.
기대하는 출력 형식은 [examples/good-diff.md](examples/good-diff.md) 를 참고.

이 보조 파일들은 자동으로 context 에 들어가지 않는다—Claude 가 필요할 때만 읽는다. 공식에서는 SKILL.md 를 500줄 이내로 유지하고, 나머지는 보조 파일에 밀어내라고 권장한다.

2. disable-model-invocation: 수동으로만 쏠 수 있는 skill

기본적으로 Claude 는 적절한 타이밍이 오면 어떤 skill 이든 알아서 호출한다. 이건 /deploy, /commit, /send-email 처럼 사이드 이펙트가 있는 명령에는 위험하다——"코드가 괜찮아 보이니까 배포해 버렸습니다" 는 사양한다.

---
name: deploy
description: 프로덕션에 배포
disable-model-invocation: true
allowed-tools: Bash(kamal deploy:*), Bash(git push:*)
---

이 한 줄을 추가하면 /deploy 는 네가 직접 입력했을 때만 발동한다. 대화 중에 Claude 가 멋대로 호출하지 않는다.

반대로 user-invocable: false 는 "Claude 만 호출할 수 있고, / 메뉴에는 나타나지 않는다" 는 설정이다. 배경 지식형 skill (예컨대 legacy-system-context. 관련 상황에서 Claude 가 자동으로 로드하지만, 사용자가 직접 쏠 이유가 없는 것) 에 맞는다.

3. context: fork: skill 을 서브에이전트에서 실행

이게 가장 큰 업그레이드다. 지난 편에서 command 가 allowed-tools: Task 로 서브에이전트를 띄울 수 있다는 얘기를 했다——다만 "Explore 서브에이전트를 띄워서……" 를 prompt 안에 직접 써 줘야 했다.

SKILL 은 frontmatter 한 줄로 해결된다:

---
name: deep-research
description: 특정 심볼 심층 조사
context: fork
agent: Explore
---

$ARGUMENTS 의 모든 사용처를 조사:
- 모든 호출 지점
- 비즈니스 시나리오
- 대체 구현

300자 이내 요약 반환.

/deep-research SomeClass 를 치면: SKILL.md 전체가 독립된 서브에이전트의 prompt 가 되고, Explore agent type 으로 실행되며, 끝나면 결론만 돌아온다. 메인 대화의 context 는 완전히 깨끗하게 유지된다.

"서브에이전트를 띄운다" 는 동작을 prompt 속 서술 문장에서 skill 의 선언적 속성으로 바꿔 놓은 셈이다.

4. paths: 파일 타입으로 자동 활성화

---
name: rails-conventions
description: Rails 프로젝트의 코딩 규약
paths: ["app/**/*.rb", "config/**/*.rb"]
---

이 프로젝트의 Rails 규약을 따를 것:
- fat controller 대신 service object 사용
- ActiveRecord scope 는 반드시 이름을 붙일 것
...

app/models/user.rb 를 편집할 때는 이 skill 이 자동으로 context 에 추가되고, package.json 을 편집할 때는 추가되지 않는다. CLAUDE.md 의 전역 배경보다 더 정밀하다——"경로별로 계층화된 CLAUDE.md" 라고 보면 된다.


실전: /plan 을 skill 로 승격하기

지난 편 마지막에 나왔던 /plan 예제는 원래 단일 파일 command 였다:

.claude/commands/plan.md

skill 로 올리면:

.claude/skills/plan/
├── SKILL.md
├── research-prompt.md     # 서브에이전트 1 지시
└── risk-prompt.md         # 서브에이전트 2 지시

SKILL.md:

---
name: plan
description: Linear ticket 기반으로 구현 방안 산출
disable-model-invocation: true
allowed-tools: mcp__linear__*, Task, Read, Grep, Bash(git log:*)
---

## 컨텍스트

@.claude/context/architecture.md

## 상태

!`git log --oneline -10`

## 태스크

Linear ticket $ARGUMENTS 의 설명과 댓글을 가져온다.

Task 로 서브에이전트 두 개를 병렬 구동:
1. 코드 조사: prompt 는 [research-prompt.md](research-prompt.md)
2. 리스크 평가: prompt 는 [risk-prompt.md](risk-prompt.md)

두 결과를 종합해 구현 단계 + 리스크 목록 + commit 단위 제안을 출력.

두 서브에이전트의 prompt 를 별도 파일에 두면, 한쪽을 고쳐도 SKILL.md 는 손대지 않아도 된다. Skill 본체는 30줄 이내로 깔끔하고, 보조 파일은 각각 수백 줄로 상세해도 상관없다.

서브에이전트를 직접 조율하기 싫다면 더 과감하게 갈 수도 있다——skill 전체를 fork 에 던져 넣고 Explore agent 가 한 번에 처리하게 하는 것:

---
name: plan
context: fork
agent: Explore
allowed-tools: mcp__linear__*
---

/plan ENG-4213 → 독립된 context 의 Explore agent 가 SKILL.md 전체를 작업으로 받아 실행 → 최종 방안만 돌아온다. 메인 대화는 조금도 더럽혀지지 않는다.


마이그레이션하지 않아도 되는 경우

모든 command 가 승격할 가치가 있는 건 아니다. .claude/commands/ 에 남겨 두는 게 나은 상황:

  • 단일 Markdown 으로 충분: 밖으로 뺄 checklist, examples, scripts 가 없다
  • fork 실행이 필요 없다: 작업이 메인 대화와 밀결합되어 있고 전체 히스토리를 봐야 한다
  • 경로 기반 자동 활성화가 필요 없다: 사용자가 수동으로만 쏜다
  • 접근 제어가 필요 없다: Claude 가 알아서 호출해도 문제 없다

단순한 /commit, /pr-desc 같은 건 frontmatter + 몇 줄짜리 prompt 다——.claude/commands/ 에 두는 게 오히려 더 직관적이다. 공식에서도 두 형태가 공존하고, 어느 쪽도 deprecate 되지 않는다 고 못박았다.


공존의 현재

지금 .claude/ 에서 가장 깔끔한 구성:

.claude/
├── commands/           # 경량: 단일 파일 + 단순 prompt
│   ├── commit.md
│   └── pr-desc.md
├── skills/             # 중량: 멀티 파일 / fork / 권한 제어
│   ├── plan/
│   │   ├── SKILL.md
│   │   ├── research-prompt.md
│   │   └── risk-prompt.md
│   ├── deploy/
│   │   └── SKILL.md    # disable-model-invocation
│   └── rails-conventions/
│       └── SKILL.md    # paths glob 자동 활성화
└── context/
    └── coding-standards.md

한꺼번에 전부 옮기지 말 것. 마이그레이션 트리거는 "이 command 가 부풀어 오르기 시작했다" 이다——본문이 200줄을 넘는다, 문서를 쪼개야 한다, 서브에이전트에 맡기고 싶다. 그 지점에 다다르면 경로 바꾸는 건 몇 분이면 끝난다.


정리

원하는 능력 commands/ skills/
단일 파일 프롬프트 숏컷
!`command` / @file / $ARGUMENTS
allowed-tools 권한 제어
보조 파일 (reference, scripts)
disable-model-invocation 오발 방지
context: fork + agent: 격리 실행
paths: glob 경로 자동 활성화

앞의 세 가지는 commands 와 skills 가 완전히 동등하고, 뒤의 네 가지는 skills 전용이다. 필요한 쪽을 고르면 된다.

공식 노선은 skills 쪽으로 기울고 있지만, commands/ 의 호환성 약속은 명확하다——이건 "당장 다 옮기지 않으면 도태된다" 는 변경이 아니라, "새 능력을 제공하지만 강제하지는 않는" 확장이다. 차이를 이해하고 필요할 때 옮기면 된다. 보이는 모양새의 일관성을 쫓을 필요는 없다.