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 는 엔트리포인트고, 같은 디렉터리에는 임의의 보조 파일을 둘 수 있다. 이런 구성을 떠올려 보자:
.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줄 이내로 유지하고, 나머지는 보조 파일에 밀어내라고 권장한다.
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 가 자동으로 로드하지만, 사용자가 직접 쏠 이유가 없는 것) 에 맞는다.
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 의 선언적 속성으로 바꿔 놓은 셈이다.
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/ 에 남겨 두는 게 나은 상황:
단순한 /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/ 의 호환성 약속은 명확하다——이건 "당장 다 옮기지 않으면 도태된다" 는 변경이 아니라, "새 능력을 제공하지만 강제하지는 않는" 확장이다. 차이를 이해하고 필요할 때 옮기면 된다. 보이는 모양새의 일관성을 쫓을 필요는 없다.