claude-dashboard v1.10~v1.13: 테마, 성능 최적화, 그리고 셸 통합까지

claude-dashboard v1.10부터 v1.13까지 6번의 릴리스에 담긴 변경사항을 정리합니다. 테마 시스템, Effort Level 표시, 성능 최적화, 그리고 터미널에서 바로 쓰는 check-ai 커맨드까지.

1. 지난 이야기

이전 글에서 Claude, Codex, Gemini, z.ai 네 가지 AI CLI의 사용량을 한 번에 조회하는 대시보드를 만들었습니다. v1.9 기준으로 핵심 기능은 완성된 상태였습니다.

🤖 Opus │ ██████░░ 80% │ $1.25 │ 5h: 42% │ 7d: 69%

하지만 실제로 매일 사용하다 보니 여러 가지 아쉬운 점이 보이기 시작했습니다:

  • 터미널 테마가 Catppuccin인데 상태줄만 파스텔톤이라 어색함
  • Opus가 High/Medium/Low effort를 지원하는데, 어떤 모드인지 안 보임
  • 긴 세션에서 상태줄 렌더링이 점점 느려짐
  • check-usage 명령어를 쓰려면 Claude Code를 실행해야 함

v1.10부터 v1.13까지, 약 2주간 6번의 릴리스를 거치며 이 문제들을 하나씩 해결했습니다.


2. 테마 시스템 (v1.12.0)

2.1 문제: 하드코딩된 색상

기존에는 ANSI 256 컬러가 코드 곳곳에 하드코딩되어 있었습니다:

// Before: 색상이 여기저기 흩어져 있음
const model = `\x1b[38;5;117m${name}\x1b[0m`;  // pastelCyan
const cost = `\x1b[38;5;222m$${amount}\x1b[0m`; // pastelYellow
const danger = `\x1b[38;5;210m${text}\x1b[0m`;  // pastelRed

문제점은 명확했습니다:

  • 색상을 바꾸려면 모든 위젯 파일을 수정해야 함
  • "모델 이름 색상"같은 의미가 코드에서 보이지 않음
  • 사용자가 선호하는 터미널 테마와 조화를 이룰 수 없음

2.2 해결: 시맨틱 컬러 역할

색상에 **의미(역할)**를 부여하는 인터페이스를 설계했습니다:

export interface ThemeColors {
  // 시맨틱 역할
  model: string;        // 모델 이름
  folder: string;       // 디렉토리, 비용
  branch: string;       // Git 브랜치
  safe: string;         // 낮은 사용량
  warning: string;      // 중간 사용량
  danger: string;       // 높은 사용량
  secondary: string;    // 보조 정보
  accent: string;       // 강조 (라벨)
  info: string;         // 정보 (파란/시안)

  // 프로그래스 바
  barFilled: string;
  barEmpty: string;
}

이제 위젯에서는 getTheme().safe처럼 역할로 색상을 참조합니다:

// After: 의미 기반 색상 참조
render(data: ModelData): string {
  return `${getTheme().model}🤖 ${data.name}${RESET}`;
  //       ↑ "모델 이름 색상"이라는 의미가 명확
}

2.3 5가지 테마

각 테마는 동일한 ThemeColors 인터페이스를 구현합니다:

// Default: 파스텔 톤 (ANSI 256)
default: {
  model: '\x1b[38;5;117m',       // pastelCyan
  safe: '\x1b[38;5;151m',        // pastelGreen
  warning: '\x1b[38;5;222m',     // pastelYellow
  danger: '\x1b[38;5;210m',      // pastelRed
}

// Catppuccin Mocha: RGB 트루컬러
catppuccin: {
  model: '\x1b[38;2;137;180;250m',    // #89b4fa blue
  safe: '\x1b[38;2;166;227;161m',     // #a6e3a1 green
  warning: '\x1b[38;2;249;226;175m',  // #f9e2af yellow
  danger: '\x1b[38;2;243;139;168m',   // #f38ba8 red
}

// Dracula: 보라톤 중심
dracula: {
  model: '\x1b[38;2;189;147;249m',    // #bd93f9 purple
  safe: '\x1b[38;2;80;250;123m',      // #50fa7b green
  warning: '\x1b[38;2;241;250;140m',  // #f1fa8c yellow
  danger: '\x1b[38;2;255;85;85m',     // #ff5555 red
}

// Gruvbox: 따뜻한 어스톤
gruvbox: {
  model: '\x1b[38;2;215;153;33m',     // #d79921 yellow
  safe: '\x1b[38;2;152;151;26m',      // #98971a green
  warning: '\x1b[38;2;250;189;47m',   // #fabd2f bright yellow
  danger: '\x1b[38;2;251;73;52m',     // #fb4934 red
}

// Minimal: 흑백 모노크롬
minimal: {
  model: '\x1b[97m',    // bright white
  safe: '\x1b[37m',     // white
  warning: '\x1b[37m',  // white
  danger: '\x1b[97m',   // bright white
}

설정은 claude-dashboard.local.json에서 한 줄이면 됩니다:

{
  "theme": "catppuccin"
}

2.4 ANSI 256 vs RGB 트루컬러

테마마다 ANSI 이스케이프 코드 방식이 다릅니다:

방식 문법 색상 수 사용 테마
ANSI 256 \x1b[38;5;{n}m 256색 default, minimal
RGB 트루컬러 \x1b[38;2;{r};{g};{b}m 1600만색 catppuccin, dracula, gruvbox
// ANSI 256: 팔레트 번호로 참조
'\x1b[38;5;117m'  // 팔레트 #117 = 파스텔 시안

// RGB 트루컬러: 정확한 색상 지정
'\x1b[38;2;137;180;250m'  // R=137 G=180 B=250 = Catppuccin Blue

RGB 트루컬러는 공식 색상 팔레트의 정확한 HEX 값을 그대로 사용할 수 있어서, Catppuccin이나 Dracula 같은 테마의 원본 색상을 왜곡 없이 재현할 수 있습니다.


3. Effort Level과 Fast Mode (v1.11.0)

3.1 모델 정보가 부족했던 기존 위젯

기존 모델 위젯은 이름만 보여줬습니다:

🤖 Opus │ ██████░░ 80% │ $1.25

하지만 Claude Code에서는 /config 명령으로 effort level(high/medium/low)을 설정할 수 있고, Opus는 /fast 모드도 지원합니다. 같은 "Opus"라도 동작이 완전히 다른데, 상태줄에서 이 정보를 알 수 없었습니다.

3.2 settings.json 읽기 + 파일 캐싱

Claude Code는 설정을 ~/.claude/settings.json에 저장합니다. 이 파일을 읽되, 상태줄은 초 단위로 갱신되므로 매번 파일을 읽으면 안 됩니다:

let settingsCache: {
  mtime: number;
  effortLevel: EffortLevel;
  fastMode: boolean;
} | null = null;

async function getModelSettings(): Promise<ModelSettings> {
  const settingsPath = join(homedir(), '.claude', 'settings.json');

  try {
    const fileStat = await stat(settingsPath);
    // ← 핵심: mtime이 같으면 파일을 다시 읽지 않음
    if (settingsCache && settingsCache.mtime === fileStat.mtimeMs) {
      return { effortLevel: settingsCache.effortLevel, fastMode: settingsCache.fastMode };
    }

    const content = await readFile(settingsPath, 'utf-8');
    const settings = JSON.parse(content);
    const effortLevel: EffortLevel = isEffortLevel(settings.effortLevel)
      ? settings.effortLevel
      : 'high';
    const fastMode = settings.fastMode === true;
    settingsCache = { mtime: fileStat.mtimeMs, effortLevel, fastMode };
    return { effortLevel, fastMode };
  } catch {
    settingsCache = null;
  }

  // 환경변수 fallback
  const envEffort = process.env.CLAUDE_CODE_EFFORT_LEVEL;
  if (isEffortLevel(envEffort)) {
    return { effortLevel: envEffort, fastMode: false };
  }

  return DEFAULT_SETTINGS;  // { effortLevel: 'high', fastMode: false }
}

stat() 호출은 readFile()보다 훨씬 가볍습니다. mtime(수정 시간)만 비교해서 파일이 변경되지 않았으면 캐시를 재사용합니다.

3.3 렌더링 규칙

모든 모델에 effort를 표시하면 의미 없는 정보가 늘어납니다. 규칙을 정했습니다:

render(data: ModelData): string {
  const shortName = shortenModelName(data.displayName);

  // Opus, Sonnet만 effort 표시 (Haiku는 제외)
  const supportsEffort = shortName === 'Opus' || shortName === 'Sonnet';
  const effortSuffix = supportsEffort
    ? `(${data.effortLevel[0].toUpperCase()})`  // 'high' → '(H)'
    : '';

  // Fast mode는 Opus 전용 (↯ = 번개 기호)
  const fastIndicator = shortName === 'Opus' && data.fastMode ? ' ↯' : '';

  return `${getTheme().model}🤖 ${shortName}${effortSuffix}${fastIndicator}${RESET}`;
}

결과:

🤖 Opus(H)      # High effort
🤖 Opus(M) ↯    # Medium effort + Fast mode
🤖 Sonnet(L)    # Low effort
🤖 Haiku        # Effort 표시 없음 (의미 없으므로)

4. 성능 최적화 (v1.12.0)

4.1 Git 호출 병렬화

projectInfo 위젯은 디렉토리명, Git 브랜치, dirty 상태, ahead/behind 정보를 보여줍니다. 문제는 이 정보를 얻기 위해 Git 명령을 3번 실행해야 한다는 것이었습니다:

// Before: 순차 실행 (~2초)
const branch = await getGitBranch(currentDir);     // ~500ms
const dirty = await isGitDirty(currentDir);         // ~500ms
const ab = await getAheadBehind(currentDir);        // ~500ms

각 호출이 독립적이므로 병렬화할 수 있습니다:

// After: 병렬 실행 (~1초)
const [branch, dirty, ab] = await Promise.all([
  getGitBranch(currentDir),
  isGitDirty(currentDir),
  getAheadBehind(currentDir),
]);

worst-case 기준 ~2초 → ~1초로 절반 단축되었습니다.

4.2 Ahead/Behind 구현

Git의 rev-list --left-right --count를 사용하면 한 번의 호출로 ahead와 behind를 모두 알 수 있습니다:

async function getAheadBehind(
  cwd: string
): Promise<{ ahead: number; behind: number } | null> {
  try {
    // ← @{u} = upstream (추적 중인 원격 브랜치)
    const result = await execGit(
      ['rev-list', '--left-right', '--count', '@{u}...HEAD'], cwd, 500
    );
    const parts = result.trim().split(/\s+/);
    if (parts.length === 2) {
      return {
        behind: parseInt(parts[0], 10) || 0,  // upstream이 앞선 커밋 수
        ahead: parseInt(parts[1], 10) || 0,    // 로컬이 앞선 커밋 수
      };
    }
    return null;
  } catch {
    return null;  // upstream이 없는 브랜치는 null
  }
}

렌더링은 간결하게:

const aheadStr = (data.ahead ?? 0) > 0 ? `↑${data.ahead}` : '';
const behindStr = (data.behind ?? 0) > 0 ? `↓${data.behind}` : '';

결과:

📁 my-project (main ↑3)       # 3개 커밋 푸시 안 됨
📁 my-project (main ↑2↓1)     # 2개 로컬, 1개 리모트
📁 my-project (main)           # 동기화됨

4.3 트랜스크립트 증분 파싱

Claude Code는 대화 내용을 JSONL 파일로 기록합니다. toolActivity, agentStatus, todoProgress 세 위젯이 이 파일을 파싱하는데, 긴 세션에서는 파일이 수 MB까지 커집니다.

기존에는 매번 전체 파일을 파싱했습니다. 바이트 오프셋 기반 증분 파싱으로 바꿨습니다:

let cachedTranscript: {
  path: string;
  size: number;        // ← 마지막 파싱 시점의 파일 크기
  data: ParsedTranscript;
} | null = null;

export async function parseTranscript(
  transcriptPath: string
): Promise<ParsedTranscript | null> {
  try {
    const fileStat = await stat(transcriptPath);
    const fileSize = fileStat.size;

    if (cachedTranscript?.path === transcriptPath
        && cachedTranscript.size <= fileSize) {
      // 파일 크기 동일 → 변경 없음, 캐시 반환
      if (cachedTranscript.size === fileSize) {
        return cachedTranscript.data;
      }

      // ← 핵심: 새로 추가된 부분만 읽어서 기존 데이터에 병합
      const newContent = await readFromOffset(
        transcriptPath, cachedTranscript.size, fileSize
      );
      processEntries(parseJsonlContent(newContent), cachedTranscript.data);
      cachedTranscript.size = fileSize;

      return cachedTranscript.data;
    }

    // 첫 파싱 또는 파일이 잘려진 경우 → 전체 파싱
    const content = await readFromOffset(transcriptPath, 0, fileSize);
    const data: ParsedTranscript = {
      entries: [],
      toolUses: new Map(),
      toolResults: new Set(),
    };
    processEntries(parseJsonlContent(content), data);
    cachedTranscript = { path: transcriptPath, size: fileSize, data };

    return data;
  } catch {
    return null;
  }
}

바이트 오프셋으로 읽는 헬퍼:

async function readFromOffset(
  filePath: string,
  offset: number,
  fileSize: number
): Promise<string> {
  const bytesToRead = fileSize - offset;
  if (bytesToRead <= 0) return '';

  const fd = await open(filePath, 'r');
  try {
    const buffer = Buffer.alloc(bytesToRead);
    await fd.read(buffer, 0, bytesToRead, offset);
    return buffer.toString('utf-8');
  } finally {
    await fd.close();
  }
}

효과: 1시간 세션(~5MB JSONL)에서 매 갱신 시 전체 파일 대신 마지막 몇 KB만 읽습니다. 체감 차이가 큽니다.


5. Widget Toggle (v1.12.0)

5.1 필요없는 위젯 숨기기

모든 사용자가 모든 위젯을 원하지는 않습니다. Codex를 안 쓰는 사람, Session ID가 필요 없는 사람 등 다양합니다. disabledWidgets 설정으로 특정 위젯을 숨길 수 있게 했습니다:

{
  "displayMode": "detailed",
  "disabledWidgets": ["codexUsage", "geminiUsage", "sessionId"]
}

구현은 Set 기반 필터링입니다:

export function getLines(config: Config): WidgetId[][] {
  const lines = config.displayMode === 'custom' && config.lines
    ? config.lines
    : DISPLAY_PRESETS[config.displayMode as keyof typeof DISPLAY_PRESETS]
      || DISPLAY_PRESETS.compact;

  const disabled = config.disabledWidgets;
  if (!disabled || disabled.length === 0) {
    return lines;
  }

  const disabledSet = new Set(disabled);
  return lines
    .map((line) => line.filter((id) => !disabledSet.has(id)))
    .filter((line) => line.length > 0);  // ← 빈 줄 자동 제거
}

핵심은 마지막 .filter((line) => line.length > 0) 입니다. 예를 들어 detailed 모드의 4번째 줄이 ['codexUsage', 'geminiUsage']인데, 둘 다 비활성화하면 빈 줄이 됩니다. 이 빈 줄을 자동으로 제거해서 레이아웃이 깨지지 않습니다.


6. Session ID 위젯 (v1.10.0)

Claude Code의 각 세션에는 UUID가 부여됩니다. 디버깅이나 로그 추적 시 세션을 식별하는 데 유용합니다:

export const sessionIdWidget: Widget<SessionIdData> = {
  id: 'sessionId',
  name: 'Session ID (Short)',
  getData: async (ctx) => {
    const sessionId = ctx.stdin.session_id;
    if (!sessionId) return null;
    return { sessionId, shortId: sessionId.slice(0, 8) };
  },
  render(data: SessionIdData): string {
    return colorize(`🔑 ${data.shortId}`, getTheme().secondary);
  },
};

// Full UUID 버전도 제공
export const sessionIdFullWidget: Widget<SessionIdData> = {
  id: 'sessionIdFull',
  name: 'Session ID (Full)',
  getData: getSessionIdData,
  render(data: SessionIdData): string {
    return colorize(`🔑 ${data.sessionId}`, getTheme().secondary);
  },
};

결과:

🔑 a1b2c3d4          # sessionId (기본: 8자)
🔑 a1b2c3d4-e5f6-... # sessionIdFull (전체 UUID)

기본 프리셋에서는 sessionId(8자)를 사용하고, 전체 UUID가 필요한 경우 sessionIdFull로 교체할 수 있습니다.


7. Setup UX 개선 (v1.12.1~v1.12.3)

7.1 인터랙티브 설정의 진화

초기 /claude-dashboard:setup은 display mode 하나만 물어봤습니다. 테마 시스템과 위젯 토글이 추가되면서 설정 항목이 늘어났고, UX를 3번에 걸쳐 개선했습니다:

v1.12.1: 테마, disabledWidgets 질문 추가

Turn 1: Display mode?
Turn 2: Theme?
Turn 3: Hide widgets?

v1.12.2: 독립 질문 배치 처리

Turn 1: Display mode + Theme + Hide widgets (한 번에)
Turn 2: Custom 모드일 때만 위젯 구성 질문

v1.12.3: 마크다운 프리뷰 추가

Turn 1: Display mode (각 옵션에 프리뷰 포함) + Language + Plan + Theme
Turn 2: Hide widgets?
Turn 3: Custom 모드 위젯 구성 (해당 시)

최종 형태에서는 display mode 선택 시 실제 출력 모습을 미리 볼 수 있습니다:

compact:
  🤖 Opus(H) │ ██░░ 80% │ $1.25 │ 5h: 42% │ 7d: 69%

normal:
  🤖 Opus(H) │ ██░░ 80% │ $1.25 │ 5h: 42% │ 7d: 69%
  📁 project (main ↑3) │ 🔑 abc123 │ ⏱ 45m │ 🔥 5K/m │ ✓ 3/5

detailed:
  🤖 Opus(H) │ ██░░ 80% │ $1.25 │ 5h: 42% │ 7d: 69%
  📁 project (main ↑3) │ 🔑 abc123 │ ⏱ 45m │ 🔥 5K/m │ ⏳ 2h │ ✓ 3/5
  CLAUDE.md: 2 │ ⚙️ 12 done │ 🤖 Agent: 1 │ 📦 85%
  🔷 codex │ 5h: 15% │ 💎 gemini │ 0%

7.2 교훈: 질문은 적을수록 좋다

처음에는 새 설정 항목이 추가될 때마다 질문을 늘렸습니다. v1.12.2에서 "독립적인 질문은 한 턴에 배치"하는 원칙을 세웠습니다. Claude Code의 AskUserQuestion 도구는 한 번에 4개까지 질문할 수 있으므로, 서로 의존성이 없는 질문은 묶어서 보냅니다.

# Bad: 4턴
Turn 1: Display mode?
Turn 2: Language?
Turn 3: Plan?
Turn 4: Theme?

# Good: 1턴 (4개 질문을 한 번에)
Turn 1: Display mode? + Language? + Plan? + Theme?

8. 셸 통합: check-ai 커맨드 (v1.13.0)

8.1 문제: Claude Code 바깥에서 사용량 확인

/claude-dashboard:check-usage는 강력하지만, Claude Code 안에서만 쓸 수 있습니다. 터미널에서 빠르게 사용량을 확인하고 싶을 때는 불편합니다.

8.2 setup-alias 커맨드

/claude-dashboard:setup-alias를 실행하면 셸 설정 파일에 check-ai 함수를 추가합니다:

# ~/.zshrc 또는 ~/.bashrc에 추가되는 함수
check-ai() {
  node "$(ls -d ~/.claude/plugins/cache/claude-dashboard/claude-dashboard/*/dist/check-usage.js 2>/dev/null | sort -V | tail -1)" "$@"
}

핵심 포인트는 동적 경로 해석입니다. 플러그인이 업데이트되면 버전 디렉토리가 바뀌는데(1.12.3/1.13.0/), 와일드카드 + sort -V로 항상 최신 버전을 찾습니다.

Windows PowerShell도 지원합니다:

function check-ai {
  $script = (Get-ChildItem "$env:USERPROFILE\.claude\plugins\cache\claude-dashboard\claude-dashboard\*\dist\check-usage.js" |
    Sort-Object { [version]$_.Directory.Parent.Name } |
    Select-Object -Last 1).FullName
  node $script $args
}

8.3 사용 예시

설정 후 어떤 터미널에서든:

# 컬러 출력
$ check-ai
════════════════════════════════════════
          CLI Usage Dashboard
════════════════════════════════════════

[Claude]
  5h: 18% (3h49m)  |  7d: 60% (1d17h)

[Codex]
  5h: 0% (4h59m)  |  7d: 3% (4d13h)  |  Plan: plus

[Gemini]
  gemini-2.5-pro    0% (23h59m)
  gemini-2.5-flash  0% (23h59m)
  ...

════════════════════════════════════════
Recommendation: codex (Lowest usage (0% used))
════════════════════════════════════════

# JSON으로 스크립팅
$ check-ai --json | jq '.recommendation'
"codex"

9. 전체 아키텍처 변화

9.1 위젯 렌더링 파이프라인

v1.12.0 이후의 렌더링 파이프라인을 정리하면:

설정 로드 → 테마 초기화 → 라인/위젯 결정 → 비활성 위젯 필터링
                                                    ↓
           출력 ← 줄 결합 ← 위젯 병렬 렌더링 ← 빈 줄 제거

코드로 보면:

// 1. 라인별 위젯을 병렬로 렌더링
async function renderLine(
  widgetIds: WidgetId[],
  ctx: WidgetContext
): Promise<string> {
  const results = await Promise.all(
    widgetIds.map((id) => renderWidget(id, ctx))
  );

  const outputs = results
    .filter((r): r is WidgetRenderResult => r !== null && r.output.length > 0)
    .map((r) => r.output);

  return outputs.join(getSeparator());  // ' │ '
}

// 2. 전체 출력 조합
export async function formatOutput(ctx: WidgetContext): Promise<string> {
  const lines = await renderAllLines(ctx);
  return lines.join('\n');
}

9.2 Display Presets

각 모드가 어떤 위젯을 포함하는지 한눈에 볼 수 있는 프리셋 정의:

export const DISPLAY_PRESETS: Record<
  Exclude<DisplayMode, 'custom'>, WidgetId[][]
> = {
  compact: [
    ['model', 'context', 'cost', 'rateLimit5h', 'rateLimit7d',
     'rateLimit7dSonnet', 'zaiUsage'],
  ],
  normal: [
    ['model', 'context', 'cost', 'rateLimit5h', 'rateLimit7d',
     'rateLimit7dSonnet', 'zaiUsage'],
    ['projectInfo', 'sessionId', 'sessionDuration', 'burnRate',
     'todoProgress'],
  ],
  detailed: [
    ['model', 'context', 'cost', 'rateLimit5h', 'rateLimit7d',
     'rateLimit7dSonnet', 'zaiUsage'],
    ['projectInfo', 'sessionId', 'sessionDuration', 'burnRate',
     'depletionTime', 'todoProgress'],
    ['configCounts', 'toolActivity', 'agentStatus', 'cacheHit'],
    ['codexUsage', 'geminiUsage'],
  ],
};

additive 원칙: 각 모드는 이전 모드의 줄을 포함하고 새 줄을 추가합니다. compact(1줄) → normal(2줄) → detailed(4줄). 위젯은 항상 같은 위치에 있어서, 모드를 바꿔도 시선이 혼란스럽지 않습니다.


10. 핵심 개념 정리

개념 적용 효과
시맨틱 컬러 역할 ThemeColors 인터페이스 색상 변경이 테마 파일 하나로 완결
파일 stat 캐싱 settings.json mtime 비교 불필요한 파일 I/O 제거
Promise.all 병렬화 Git 호출, 위젯 렌더링 레이턴시 50% 감소
바이트 오프셋 증분 파싱 JSONL 트랜스크립트 긴 세션에서도 일정한 파싱 속도
Set 기반 필터링 disabledWidgets O(1) 조회로 위젯 토글
Additive 프리셋 compact → normal → detailed 모드 전환 시 일관된 레이아웃
동적 경로 해석 check-ai alias의 와일드카드 플러그인 업데이트에 자동 대응

11. 버전별 변경 요약

버전 주요 변경 카테고리
v1.10.0 Session ID 위젯 기능
v1.10.1 문서/README 정리 문서
v1.11.0 Effort Level (H/M/L) + Fast Mode (↯) 기능
v1.12.0 테마 시스템 (5종), Widget Toggle, Git ↑↓, 증분 파싱, 성능 2x 기능/성능
v1.12.1~3 Setup 인터랙티브 UX 개선 (배치 질문, 프리뷰) UX
v1.13.0 setup-alias (check-ai 셸 커맨드) 기능

12. FAQ

Q: 테마를 커스텀으로 만들 수 있나요?

A: 현재는 5가지 내장 테마만 지원합니다. ThemeColors 인터페이스를 구현하면 새 테마를 추가할 수 있지만, 사용자 정의 테마 설정 파일은 아직 지원하지 않습니다. 향후 추가를 검토하고 있습니다.

Q: Fast Mode (↯)는 무엇인가요?

A: Claude Code에서 /fast를 활성화하면 같은 Opus 모델에서 더 빠른 출력을 제공합니다. 상태줄에서 ↯ 기호로 이 모드가 활성화되어 있는지 한눈에 확인할 수 있습니다.

Q: check-ai 별칭이 플러그인 업데이트 후에도 작동하나요?

A: 네. check-ai 함수는 와일드카드와 sort -V를 사용해 항상 최신 플러그인 버전의 스크립트를 찾습니다. 업데이트 후 별도 설정이 필요 없습니다.

Q: 증분 파싱은 어떤 경우에 전체 파싱으로 돌아가나요?

A: 두 가지 경우입니다. (1) 처음 파싱할 때, (2) 파일이 잘려서 기존 캐시 크기보다 작아졌을 때. 일반적인 사용에서 (2)는 거의 발생하지 않습니다.

Q: 성능 개선 효과를 체감할 수 있나요?

A: Git 병렬화는 모든 사용자에게 체감됩니다 (~2초 → ~1초). 증분 파싱은 30분 이상 긴 세션에서 효과가 두드러집니다. 짧은 세션에서는 차이를 느끼기 어렵습니다.


13. 참고 자료


14. 다음 단계

6번의 릴리스를 통해 보이는 것(테마, Effort Level), 보이지 않는 것(성능, 증분 파싱), 밖에서 쓰는 것(check-ai)을 모두 개선했습니다. 다음은 세션 목록 조회 기능을 작업할 예정입니다.

시리즈 목차:

  1. Claude Dashboard 플러그인 개발기
  2. Claude Code, Codex, Gemini 통합 사용량 대시보드
  3. v1.10~v1.13: 테마, 성능 최적화, 셸 통합 ← 현재 글
  4. 세션 목록과 히스토리 관리 (예정)