1편의 잔상

Claude Code Switcher (CCS) — 프로바이더 전환·멀티 계정·로컬 모델까지 한 명령으로표면의 글이었다. CCS 가 등장했는지, 무엇을 풀고 있는지, 누구에게 언제 맞고 안 맞는지. 그 글의 한 단락을 그대로 옮기면 이렇다.

CCS 는 Claude Code 가 환경변수 두 개로 정의되는 도구라는 사실을 그대로 받아들인 도구다. 그 두 변수를 프로파일 이라는 단위로 정리하고, OAuth 우회와 다중 계정까지 한 명령 표면 위로 올려놓았다.

2편은 그 표면 아래 다. ~/.ccs/ 한 디렉토리에 무엇이 어떻게 사는지, config.yaml 의 어느 키가 무엇을 결정하는지, instance 와 shared 가 어떻게 갈리는지, 로컬 프록시는 정확히 어떤 변환을 거치는지. 1편에서 표 한 줄로만 끝난 부분을 시스템적 그림으로 풀어 본다.

이 글의 모든 디렉토리·키 이름은 사용자 머신의 실제 CCS 설치를 직접 들여다본 결과다. 토큰·세션 같은 민감한 값은 보지 않고 구조만 인용한다.


~/.ccs/ 한 장 지도

CCS 가 머신에 자리 잡으면 홈 디렉토리에 ~/.ccs/ 한 폴더가 생긴다. 그 안의 트리가 사실상 CCS 의 운영 모델 전부다 — 다만 진짜 그림은 심볼릭 링크 체인 이다.

~/.claude/                       ← 사용자의 기존 Claude Code 글로벌 (canonical)
├── settings.json
├── commands/, skills/, agents/, plugins/
└── projects/

~/.ccs/                          ← CCS 영역
├── config.yaml                  ← 메인 설정 (YAML, 11K 정도)
├── .session-secret              ← 세션 비밀 (64B, 절대 공유 금지)
├── .claude/                     ← CCS *번들* (ccs.md, ccs-delegation 스킬 등 자체 주입)
├── cliproxy/bin/                ← CLIProxyAPI 바이너리 (자동 다운로드)
├── cache/, completions/, logs/
│
├── shared/                      ← 사용자 정의 공유 영역
│   ├── settings.json    → ~/.claude/settings.json   (symlink)
│   ├── commands         → ~/.claude/commands         (symlink)
│   ├── skills           → ~/.claude/skills           (symlink)
│   ├── agents           → ~/.claude/agents           (symlink)
│   ├── plugins          → ~/.claude/plugins          (symlink)
│   └── context-groups/default/projects/              (실 디렉토리)
│
└── instances/                   ← per-account 격리 영역
    ├── team/                    ← 회사 Team account
    │   ├── settings.json   → ~/.ccs/shared/settings.json     (symlink)
    │   ├── commands        → ~/.ccs/shared/commands           (symlink)
    │   ├── skills          → ~/.ccs/shared/skills             (symlink)
    │   ├── agents          → ~/.ccs/shared/agents             (symlink)
    │   ├── projects        → ~/.ccs/shared/context-groups/default/projects/  (symlink)
    │   ├── plugins/, .anthropic/, sessions/, session-env/    ← 실 디렉토리
    │   ├── todos/, file-history/, shell-snapshots/, logs/    ← 실
    │   ├── backups/, cache/, debug/, image-cache/, paste-cache/, plans/   ← 실
    │   └── .claude.json, history.jsonl, .session-stats.json,
    │       mcp-needs-auth-cache.json, policy-limits.json, remote-settings.json
    └── enterprise/              ← 회사 Enterprise account (구조 동일)

핵심 두 가지를 짚어둔다.

1. 2단계 symlink 체인instances/team/settings.json 을 따라가면 ~/.ccs/shared/settings.json 으로 가고, 그게 다시 ~/.claude/settings.json 으로 간다. 즉 사용자의 기존 Claude Code 글로벌 설정이 모든 인스턴스에 자동 반영된다. 한 번 만든 슬래시 커맨드·스킬·MCP 가 team / enterprise 양쪽에서 자동으로 살아 있는 이유.

2. projects/ 만 한 단계 — 대화 히스토리는 instances/team/projects~/.ccs/shared/context-groups/default/projects/ (실 디렉토리) 까지만 간다. ~/.claude/projects/ 까지 체인되지 않는다 — CCS 는 자기 context-group 단위로 대화를 모아 두고, 사용자의 평소 Claude Code 대화와는 자연히 분리시켜 둔다. 회사 대화가 평소 개인용 Claude Code 에 섞이지 않도록 의도된 끊음.

이 2단계 체인이 이중 구조 라고 표현했던 것의 실제 메커니즘이고, 1편의 “공유 vs 분리” 가 단일 축이 아니라 어느 단위까지 체인을 잇느냐 의 문제였다는 뜻이다. §5–6 에서 이 단위들을 풀어 본다.


config.yaml 의 9개 영역

설정의 거의 전부는 ~/.ccs/config.yaml 한 파일에 모인다. JSON 이 아니라 YAML 인 점이 의외인데, 사람이 손으로 읽고 고치기 좋은 쪽으로 의도된 선택으로 보인다.

키 구조만 추리면 9개 영역으로 나뉜다.

version:                # 메타
default:                # 기본 프로파일

accounts:               # *instance 단위* 메타
  team:
    context_mode:       # isolated | shared
    context_group:      # shared 일 때 그룹 이름
    continuity_mode:    # 추가 공유 정책
  enterprise:
    ...

profiles:               # 프로바이더 프로파일 (glm, kimi, ollama, ...)

cliproxy:               # OAuth 프록시 백엔드 설정
  backend:
  oauth_accounts:
  providers:
  routing:
    strategy:           # round-robin / fill-first
    session_affinity:
    session_affinity_ttl:

proxy:                  # 로컬 OpenAI-호환 프록시
  profile_ports:        # 프로파일별 포트 매핑
  routing:
    longContextThreshold:

cliproxy_server:        # 원격/로컬 CLIProxy 서버
  remote: { enabled, host, protocol, auth_token }
  fallback: { enabled, auto_start }
  local:   { port, auto_start }

logging:                # 로그 회전·보관
preferences:            # theme, telemetry, auto_update

websearch:              # ★ 8개 fallback 프로바이더
  providers:
    exa, tavily, brave, searxng, duckduckgo, gemini, opencode, grok

# --- 통합 (Claude Code 외부 도구들) ---
copilot:                # GitHub Copilot 라우팅
cursor:                 # Cursor 통합 (ghost_mode 포함)
channels:               # Telegram/Discord/iMessage
thinking:               # opus/sonnet/haiku 별 thinking 기본값
global_env:             # DISABLE_TELEMETRY 등 전역 env 자동 주입

이 9개를 한 번 훑으면 CCS 가 단순 스위처가 아니라 Claude Code 주변의 운영 표면 전체 를 흡수해 가고 있다는 인상이 분명해진다. §12 에서 이 중 안 알려진 부분을 따로 추린다.


instances/<name>/ 디렉토리는 언뜻 보면 Claude Code 의 모든 상태를 담은 통 같지만, ls -la 로 열어 보면 들어 있는 항목 절반은 사실 shared/ 로의 symlink, 나머지 절반만 인스턴스별 실 상태다. 둘을 갈라 두면 멘탈 모델이 명확하다.

Symlink 항목 (모든 인스턴스가 공유):

항목가는 곳
settings.json~/.ccs/shared/settings.json~/.claude/settings.json
commands/~/.ccs/shared/commands~/.claude/commands/
skills/~/.ccs/shared/skills~/.claude/skills/
agents/~/.ccs/shared/agents~/.claude/agents/
projects/~/.ccs/shared/context-groups/default/projects/ (한 단계만)

settings.json 의 최상위 키 9개는 (체인을 따라가 본 결과) 다음과 같다 — 사용자의 기존 ~/.claude/settings.json 의 스키마 그대로다.

alwaysThinkingEnabled
enabledPlugins
env
extraKnownMarketplaces
hooks
permissions
skipAutoPermissionPrompt
skipDangerousModePermissionPrompt
statusLine

인스턴스별 실 상태 (격리된 자기 데이터):

분류항목
인증 (절대 안 섞임).anthropic/ (OAuth), .claude.json (사용자 메타)
세션·실행sessions/, session-env/, shell-snapshots/, history.jsonl
작업 상태todos/, file-history/, plans/ (team 만), backups/
로깅·캐시logs/, cache/, debug/, image-cache/, paste-cache/
플러그인plugins/ — 인스턴스별 (claude-hud 같은 설치)
메타·정책policy-limits.json, remote-settings.json, mcp-needs-auth-cache.json, .session-stats.json
외부 통합.omc/ (oh-my-claudecode 상태)

흥미로운 사실 — 이 글의 plan 파일은 instances/team/plans/ccs-hashed-llama.md 에 있다 (이 항목은 real, symlink 아님). 이 글을 쓰는 동안 사용자의 Claude Code 세션은 team 인스턴스에서 돌고 있었다는 뜻이다. CCS 는 보이지 않게 CLAUDE_CONFIG_DIR 같은 환경변수로 인스턴스 컨텍스트를 끼워 넣는다.

enterprise 인스턴스는 team 보다 가볍게 비어 있는 상태였는데, 활성 사용 중인지 아닌지 가 디렉토리 충실도에 그대로 드러난다. CCS 는 인스턴스를 만들 때 symlink 와 빈 디렉토리만 만들어 두고, 실제로 사용해야 실 데이터가 채워지는 lazy 한 모델이다.


shared/ 와 context-groups — 1편의 빈칸 메우기

1편의 “설정·대화는 공유하면서 계정만 분리하고 싶다면” 단락의 진짜 답 이 여기 있다. CCS 는 이 시나리오를 shared/ 라는 별도 영역과 context_mode/context_group 설정으로 정식 지원한다.

config.yamlaccounts: 섹션:

accounts:
  team:
    context_mode: isolated          # 또는 shared
    context_group: default          # shared 일 때만 의미
    continuity_mode: deeper         # 또는 default
  enterprise:
    context_mode: shared
    context_group: default
    continuity_mode: deeper

세 키의 역할:

  • context_mode: isolated | shared — 인스턴스가 자기 디렉토리에만 사느냐, 같은 그룹의 다른 인스턴스와 공유하느냐
  • context_groupshared 일 때 어느 그룹 이름인지. ~/.ccs/shared/context-groups/<group>/ 가 그 그룹의 공유 데이터 자리
  • continuity_mode: deeper — 공유의 깊이. deeper 는 더 많은 디렉토리를 공유 대상에 포함

이 셋의 조합으로 1편에서 “공유 vs 분리” 라고 단순화한 축이 사실 세 축의 조합 임이 드러난다.


무엇이 공유 가능하고 무엇이 항상 격리되는가

CCS 의 공식 docs 는 이 부분을 못 박아 둔다.

CCS only shares workspace context paths (project/session context files). It does not merge or copy authentication credentials between accounts.

docs/session-sharing-technical-analysis.md

요약 표:

분류대상동작
shared (조건부)session-env/shared + deeper 일 때 그룹 안에서 공유
shared (조건부)file-history/동일
shared (조건부)shell-snapshots/동일
shared (조건부)todos/동일
항상 격리.anthropic/ (OAuth)인증은 어떤 모드에서도 인스턴스별
항상 격리인증 토큰 / 자격증명동일
수동 공유commands/, skills/, agents/, plugins/shared/<dir>/ 에 두면 모든 인스턴스가 사용

핵심 한 줄 — 인증은 절대 안 섞인다. 한 인스턴스의 OAuth 토큰이 실수로 다른 인스턴스의 요청에 흘러갈 일이 구조적으로 없다. 회사 환경에서 가장 큰 사고 가능성을 시스템 자체가 막아 둔 셈이다.

ccs -r (resume) 는 현재 활성 lane 만 이어가고, ccs <account> -r 은 그 인스턴스의 lane 만 이어간다. 두 인스턴스 모두 다른 continuity 인벤토리를 가질 수 있다는 점을 의식하고 운영해야 한다.


4가지 진입점 — Target Adapter System

ccs 가 Claude Code 만 호출하는 게 아니다. CCS 는 runtime 자체를 바꾸는 4가지 바이너리를 노출한다.

바이너리runtime
ccsClaude Code (기본)
ccsd / ccs-droidFactory Droid
ccsx / ccs-codexCodex CLI (네이티브)
ccsxpCodex CLI + CLIProxy 프로바이더 오버라이드

내부적으로는 각 바이너리가 CCS_INTERNAL_ENTRY_TARGET 환경변수를 세팅한 후 target resolver 에 위임한다. resolver 의 우선순위:

  1. CLI 플래그 (--target)
  2. 진입 바이너리 자체
  3. argv[0] 이름 검출
  4. 프로파일별 config
  5. 기본값

이 추상화 덕분에 “Claude Code 로 GLM 돌리기” 와 “Droid 로 GLM 돌리기” 가 같은 명령 표면 위에서 가능하다 — ccs glm vs ccs --target droid glm. Codex 의 경우 자기 ~/.codex/ 상태를 별도 보존하기 위해 환경변수만 임시로 덮어쓰는 식의 신경을 더 쓴다.


로컬 프록시 (127.0.0.1:포트) 의 5단계 흐름

ccs glm 같은 OpenAI-호환 프로바이더 명령이 들어오면 다음 5단계를 거친다 (docs/openai-compatible-providers.md 정리).

  1. 127.0.0.1 에서 그 프로파일의 로컬 포트 바인딩 (포트는 proxy.profile_ports 에서 할당)
  2. Claude Code 가 보내는 Anthropic 형식 /v1/messages 요청 수신
  3. OpenAI chat-completions 형식으로 변환
  4. 업스트림 프로바이더로 포워드
  5. 스트리밍 응답을 다시 Anthropic SSE 로 역변환해 Claude Code 에 돌려줌

요점 — Claude Code 는 자기가 Anthropic 과 이야기하는 줄 안다. 변환은 프록시 한 점에서만 일어나고, 한 자리에서 처리하니 디버깅 지점도 한 곳이다.

Anthropic-호환 엔드포인트는 프록시를 우회한다 — 즉 https://api.anthropic.com 이나 Z.AI 의 /api/anthropic 으로 가는 요청은 변환이 필요 없으니 그냥 직통. 이 분기 덕분에 GLM 사용은 추상화 비용이 거의 0 이다.

CLIProxyAPI 서버 자체는 기본적으로 port 8317 에서 동작하는데, cliproxy_server.local.port 로 바꿀 수 있고 remote.host 를 켜면 다른 머신의 CLIProxy 를 쓸 수도 있다.


시나리오 라우팅 4종

CCS 가 수동으로 프로바이더를 명시하지 않아도 자동으로 라우팅하는 4가지 시나리오가 있다.

시나리오트리거
background요청이 Haiku 를 포함 (가벼운 백그라운드 작업)
thinkAnthropic extended thinking 활성화
longContext토큰 추정치가 임계값 초과 (proxy.routing.longContextThreshold)
webSearchweb_search 툴 호출

config.yamlproxy.routing.longContextThreshold 키가 실제로 있어 임계값을 손으로 조절할 수 있다. 자동화의 깊이는 워크로드에 따라 잘 맞기도, 헷갈리기도 한다 — 처음부터 라우팅 룰을 복잡하게 짜기보다 한 주 단순 운영 후 패턴을 보고 추가 하는 편이 낫다 (1편 팁 §7 참조).


CLIProxy 서브시스템 — OAuth 프로바이더의 자리

API 키가 없는 OAuth 기반 프로바이더 — Gemini, GitHub Copilot, AWS Kiro 등 — 는 별도 서브시스템이 책임진다. ~/.ccs/cliproxy/bin/ 에 자동 다운로드되는 CLIProxyAPI 바이너리가 그 핵심이다.

이 서브시스템이 다루는 것들:

  • Account manager — OAuth 토큰 lifecycle (발급·갱신·만료)
  • Quota manager — 프로바이더별 quota 추적, 자동 failover
  • Quota fetcher — 사용량 실시간 동기화
  • Auth handler — 각 프로바이더의 OAuth 흐름 (Anthropic, Gemini, Copilot, Kiro 등)
  • Model catalogs — 프로바이더별 모델 목록 + compatibility 가드 (codex-plan-compatibility.ts 같은 파일이 plan 별로 안 되는 모델 조합 을 막아준다)
  • Hybrid quota strategyround-robin 또는 fill-first (cliproxy.routing.strategy) — 같은 프로바이더의 여러 OAuth 계정을 어떻게 분산시킬지

config.yamlcliproxy.oauth_accounts 가 등록된 OAuth 계정 목록을 담는데, 이 부분은 토큰을 포함하므로 git 에 올리면 안 되는 영역이다. 다음 §11 에서 정리.


§2 의 symlink 체인을 본 다음에 던질 만한 자연스러운 질문 — 내 CCS 환경을 다른 머신에 어떻게 옮기나? ~/.ccs/ 통째로 git 에 넣으면 되나? 답은 "~/.ccs/ 를 versioning 하는 게 아니라, ~/.claude/ 를 versioning 하면 CCS 가 그쪽으로 끌어들인다" 다.

멘탈 모델: ~/.claude/ 가 source of truth

체인의 시작점은 ~/.claude/ 다. CCS 의 shared/ 와 instance 들은 그쪽으로 가는 포인터 일 뿐. 따라서 dotfiles 의 핵심은 다음 한 줄로 압축된다.

~/.claude/ 를 git 으로 관리하면, 그 머신에 CCS 를 새로 설치하더라도 shared/ 만 동일한 symlink 로 다시 걸어 두면 모든 인스턴스가 자동으로 같은 환경.

git 에 올리기 좋은 자산 (~/.claude/ 쪽)

  • ~/.claude/commands/ — 슬래시 커맨드
  • ~/.claude/skills/ — 스킬
  • ~/.claude/agents/ — 에이전트 정의
  • ~/.claude/CLAUDE.md — 글로벌 메모리
  • ~/.claude/settings.json주의: 안에 토큰을 직접 적어 두지 않은 경우만. env 필드에 비밀이 있으면 분리 (settings.local.json 패턴)
  • ~/.ccs/shared/context-groups/default/ — CCS 의 그룹 정의 자체 (실 디렉토리). 안에 들어 있는 projects/ 는 빼고 그룹 메타만 versioning 하는 식.

~/.ccs/.claude/ (CCS 번들) 은 npm 패키지 일부라 install 시 자동으로 따라온다. 직접 versioning 할 필요 없음.

절대 git 금지 (자격증명·세션·트랜스크립트)

  • ~/.ccs/config.yamlOAuth 토큰·refresh_token 포함. 머신별로 보관.
  • ~/.ccs/.session-secret — 세션 비밀.
  • ~/.ccs/instances/*/.anthropic/ — Anthropic OAuth.
  • ~/.ccs/instances/*/.claude.json — 사용자 메타 (계정 식별자 포함).
  • ~/.ccs/instances/*/sessions/, session-env/ — 세션·세션별 env (토큰 환경변수 가능).
  • ~/.ccs/instances/*/history.jsonl, file-history/ — 명령·파일 history (회사 코드 흔적).
  • ~/.ccs/shared/context-groups/*/projects/대화 히스토리 본체. PII·회사 코드 포함 가능.
  • ~/.ccs/cliproxy/, cache/, logs/ — 바이너리·휘발성.
  • ~/.claude/projects/ — 평소 Claude Code 대화 히스토리 (위와 동일 이유).

새 머신 부트스트랩 4단계

dotfiles 가 ~/.claude/ 만 들고 있다고 할 때 새 머신에서 같은 환경을 만들려면:

  1. dotfiles clone — ~/.claude/ 가 채워짐
  2. npm install -g @kaitranntt/ccs — CCS 설치
  3. ccs config — 초기 ~/.ccs/ 골격 생성
  4. symlink 재구성~/.ccs/shared/{commands,skills,agents,plugins,settings.json}~/.claude/ 의 동명 항목으로 link. 한 줄 스크립트로 자동화 권장.
ln -sf ~/.claude/commands       ~/.ccs/shared/commands
ln -sf ~/.claude/skills         ~/.ccs/shared/skills
ln -sf ~/.claude/agents         ~/.ccs/shared/agents
ln -sf ~/.claude/plugins        ~/.ccs/shared/plugins
ln -sf ~/.claude/settings.json  ~/.ccs/shared/settings.json

이 다섯 줄로 새 머신에서도 1편의 instance 들이 평소 환경을 그대로 본다. OAuth 인증만 각 인스턴스에서 새로 하면 끝.

격리의 운영 이점

  • 한 인스턴스가 망가져도 (plugins/ 깨짐, policy-limits.json 누락 등) 다른 인스턴스는 그대로. 단일 장애점이 줄어드는 부수 효과.
  • 인스턴스 단위 백업 — tar czf team-backup.tgz -C ~/.ccs/instances team (symlink 는 그대로 보존하는 옵션 -h 추가 시 따라가서 archive). 보통은 symlink 그대로 두는 게 의미 있다 (복원 시 같은 dotfiles 환경을 가정).

놀라운 발견 모음 — config.yaml 의 비공식 영역들

CCS 의 README 가 강조하지 않지만 config.yaml 키만 봐도 드러나는, 단순 “프로바이더 스위처” 라기에는 풍부한 통합들.

  • WebSearch 8 프로바이더 fallbackwebsearch.providers 아래에 exa, tavily, brave, searxng, duckduckgo, gemini, opencode, grok 8개. Claude Code 의 WebSearch 가 Anthropic 외 8곳으로 fallback 가능하다는 의미. docs/websearch.md 가 별도 문서로 있을 정도로 일급 통합.
  • GitHub Copilot 라우팅copilot.account_type, copilot.rate_limit, copilot.model. CCS 가 Copilot 도 프로바이더 한 슬롯 으로 다룬다.
  • Cursor 통합cursor.ghost_mode, cursor.port 등. Cursor 의 ghost-mode 까지 다루는 건 CCS 의 야심을 보여준다.
  • Channels (Telegram/Discord/iMessage)channels.selected, channels.unattended. 폰의 메신저로 Claude 에게 일을 시키는 통합 (1편의 free-claude-code 텔레그램 봇 사례 와 같은 결).
  • Thinking tier_defaultsthinking.tier_defaults.opus / sonnet / haiku 별로 thinking 기본 모드를 다르게 잡을 수 있다. Opus 는 항상 deep thinking, Haiku 는 끄기, Sonnet 은 상황에 따라 — 식의 정책이 가능.
  • Global env 자동 주입global_env.env.DISABLE_BUG_COMMAND, DISABLE_ERROR_REPORTING, DISABLE_TELEMETRY. 보안·프라이버시에 민감한 회사 환경에서 자동으로 텔레메트리를 끄게 하는 헬퍼.
  • session_affinitycliproxy.routing.session_affinitysession_affinity_ttl. 같은 세션은 같은 OAuth 계정으로 라우팅되도록 sticky 하게 묶는 정책.

이런 영역들은 CCS 가 단순 스위처에서 운영 플랫폼 으로 진화 중이라는 신호다.


한 줄로

1편이 CCS 의 표면이었다면 2편은 안쪽이고, 안쪽을 보고 나면 1편의 단순 광고문 — “프로파일로 옮겨다닌다” — 이 사실은 instance 격리 + shared 공유 + OAuth 분리 + OpenAI ↔ Anthropic 변환 + 시나리오 라우팅 + 통합 점들 의 다섯 축으로 짜여 있다는 게 보인다. 추상화가 두껍다는 1편의 단점 평가는 그래서 정확하지만, 그 두께가 풀고 있는 문제도 같이 두껍다는 사실이 디렉토리 한 통에 적나라하다.

다음 편이 있다면 두 갈래 중 하나로 갈 가능성이 크다 — (a) proxy.routing 시나리오를 실제 워크로드로 맞춰 보는 실측 운영 글, (b) CCS + dotfiles + Anthropic Workspaces 의 멀티-머신 계정 운영 글. 어느 쪽이 먼저 나올지는 다음 글에서 정한다.


참고