---
name: download
description: Gist의 mccm.json을 로컬 settings.json에 적용한다. "/download", "환경 다운로드", "download env" 등의 요청에 사용한다.
allowed-tools: Bash, Read, Edit
---

## 현재 상태

- mccm.json (gist): !`GIST_ID=$(gh api gists --jq '.[] | select(.files["mccm.json"] != null) | .id' 2>/dev/null | head -1); [ -n "$GIST_ID" ] && gh gist view "$GIST_ID" --filename mccm.json 2>/dev/null || echo "not found — gist에 mccm.json 파일이 없습니다"`
- settings.json: !`cat "$HOME/.claude/settings.json" 2>/dev/null || echo "not found"`
- ccstatusline: !`cat "$HOME/.config/ccstatusline/settings.json" 2>/dev/null || echo "not found"`

## 변수 치환 규칙

mccm.json의 문자열 값에서 아래 변수를 치환한다:
- `${HOME}` → 현재 OS의 홈 디렉토리 (`$HOME` 또는 `$USERPROFILE`)
- `${USER}` → 현재 사용자명

## 작업 지침

Gist의 mccm.json을 기준으로 로컬 환경을 구성한다.

### 0. gist 확인

`gh api gists`로 `mccm.json`을 포함하는 gist를 검색한다.

- **0개**: 사용자에게 안내한다 — `gh gist create --desc "mccm env"` 으로 mccm.json gist를 먼저 생성하라고.
- **1개**: 해당 gist를 사용한다.
- **2개 이상**: ID와 description을 목록으로 보여주고 사용자에게 선택을 받는다. `head -1`으로 자동 선택하지 않는다.

### 1. 마켓플레이스

mccm.json의 `marketplaces`를 settings.json의 `extraKnownMarketplaces`와 비교한다.
- 미등록 마켓플레이스: `claude plugin marketplace add` 실행
- 등록 완료: `claude plugin marketplace update` 실행

### 2. 플러그인

mccm.json의 `plugins`를 settings.json의 `enabledPlugins`와 비교한다.
- 미설치: `claude plugin install` 실행
- 기설치: `claude plugin update` 실행

### 3. CLI 도구

mccm.json의 `clis`를 확인한다.

**⚠️ 보안:** `check`와 `install` 값은 셸 명령으로 실행되므로, 실행 전 반드시 사용자에게 명령 전문을 보여주고 승인을 받는다.

각 CLI 항목에 대해:
1. `check`와 `install` 명령을 사용자에게 표시한다
2. 사용자가 승인하면 `check` 명령으로 설치 여부를 판단한다
3. 미설치: 사용자가 승인한 `install` 명령 실행
4. 기설치: 스킵

사용자가 거부한 항목은 스킵하고 완료 보고에 "스킵됨"으로 기록한다.

### 4. ccstatusline 설정 복원

mccm.json에 `ccstatusline.config` 객체가 있으면 위젯 디자인 본체를 `$HOME/.config/ccstatusline/settings.json`에 복원한다. (바이너리 설치는 3단계 `clis`에서, statusLine 연결은 5단계 `settings`에서 처리되므로 여기서는 디자인 본체만 다룬다.)

> **⚠️ 글리프 주의 (필독):** `ccstatusline.config`의 `powerline.separators / startCaps / endCaps` 에는 화면상 **빈 칸으로 보이는 Nerd Font PUA 글리프**(U+E0B0~U+E0BF 등)가 들어 있다. Read·Edit·수기 전사로 다루면 글리프가 보이지 않아 **빈 문자열로 소실**된다.
> - 이 파일은 **반드시 아래 `jq` 파이프로 전체 덮어쓰기**한다. Read·Edit·수기 전사 금지.
> - "한 필드만 다른 것 같아도" 부분 Edit 하지 말고 **무조건 전체를 `jq`로 다시 쓴다** (gist가 이 파일의 단일 진실원).
> - 동일/다름 판정도 눈이 아니라 **바이트로** 한다(아래 `diff`).

- 로컬 파일이 **없으면**: `mkdir -p`로 디렉토리를 만든 뒤 바로 기록한다.
- 로컬 파일이 mccm.json `config`와 **동일**하면(아래 `diff`가 무출력): 스킵.
- **다르면(충돌)**: 사용자에게 차이를 보여주고 물어본다:
  - **(1) 취소** (기존 로컬 설정 유지) / **(2) 대치** (gist 설정으로 교체)
  - 사용자 선택에 따라 적용한다.

```bash
GIST_ID=<선택된 gist id>
DST="$HOME/.config/ccstatusline/settings.json"

# 동일/다름 판정 (바이트 비교 — 보이지 않는 글리프 차이까지 잡힘)
diff <(gh gist view "$GIST_ID" --filename mccm.json | jq -S '.ccstatusline.config') \
     <(jq -S . "$DST" 2>/dev/null) >/dev/null \
  && { echo "동일 → 스킵"; } \
  || echo "다름 → (사용자 확인 후) 대치"

# 대치(또는 신규): 전체 덮어쓰기로만 기록 — 절대 전사/Edit 하지 않는다 (글리프 보존)
mkdir -p "$HOME/.config/ccstatusline"
gh gist view "$GIST_ID" --filename mccm.json | jq '.ccstatusline.config' > "$DST"

# 기록 후 검증: 로컬이 gist와 바이트 동일한지 재확인 (글리프 포함)
diff <(gh gist view "$GIST_ID" --filename mccm.json | jq -S '.ccstatusline.config') \
     <(jq -S . "$DST") >/dev/null \
  && echo "검증 OK (글리프 포함 일치)" \
  || echo "⚠️ 불일치 — jq 파이프로 재시도(전사 금지)"
```

### 5. settings

mccm.json의 `settings`를 settings.json과 비교한다. 변수 치환 적용 후 비교.

**충돌이 없는 경우** (mccm.json에만 있는 키): 바로 추가.

**충돌이 있는 경우** (같은 키에 다른 값): 사용자에게 물어본다:
- 각 충돌 항목을 표시: `키: mccm.json 값 vs settings.json 값`
- 선택지 제시: **(1) 취소** (기존 유지) / **(2) 대치** (mccm.json 값으로 교체)
- 사용자 선택에 따라 적용

> `statusLine` 블록이 mccm.json에 있으면 settings.json에 반영한다. 단 이 블록은 ccstatusline 바이너리가 PATH에 있어야 정상 동작하므로, 3단계에서 ccstatusline 설치를 건너뛴/거부한 경우 statusLine 적용도 보류하고 사용자에게 알린다.

### 6. mcpServers

mccm.json의 `mcpServers`를 settings.json의 `mcpServers`와 비교한다. 변수 치환 적용.

- 새 서버: 바로 추가
- 기존 서버와 충돌: 5단계와 동일하게 사용자에게 물어본다

### 7. hooks

mccm.json의 `hooks`를 settings.json의 `hooks`와 비교한다. 변수 치환 적용.

**⚠️ 보안:** hooks는 Claude Code 세션에서 자동 실행되므로, 새 hook과 충돌 hook 모두 사용자 확인이 필수이다.

- 새 hook: event, matcher, command를 보여주고 사용자 승인 후 추가
- 기존 hook과 충돌 (동일 event + matcher에 다른 command): 사용자에게 물어본다

사용자가 거부한 hook은 스킵하고 완료 보고에 "스킵됨"으로 기록한다.

### 8. 로컬 전용 항목 감지 (완전 동기화)

settings.json에는 있지만 mccm.json에는 없는 항목을 감지한다:

- `enabledPlugins`에 있지만 mccm.json `plugins`에 없는 플러그인
- `mcpServers`에 있지만 mccm.json `mcpServers`에 없는 서버
- `hooks`에 있지만 mccm.json `hooks`에 없는 hook

감지된 항목이 있으면 사용자에게 목록을 보여주고 물어본다:
> 아래 항목은 로컬에만 존재하고 Gist에는 없습니다:
> - (항목 목록)
>
> **(1) 유지** (로컬에만 남김) / **(2) 삭제** (Gist 기준으로 완전 동기화)

사용자가 **(2) 삭제**를 선택하면:
- 플러그인: `claude plugin uninstall` 실행
- mcpServers, hooks: settings.json에서 제거

### 9. settings.json 저장

모든 변경을 적용하여 `$HOME/.claude/settings.json`에 저장한다.

Read 도구로 파일을 읽고, 변경 사항을 Edit 도구로 적용한다.

### 10. 완료 보고

- 등록/업데이트된 마켓플레이스
- 설치/업데이트된 플러그인
- 복원된 ccstatusline 설정 (라인/항목 수, 충돌 해결 결과)
- 병합된 settings 항목
- 추가된 mcpServers
- 추가된 hooks
- 설치된 CLI 도구
- 충돌 해결 결과
- 삭제된 로컬 전용 항목 (있는 경우)
