WIKIClaude Usage Crab
개인 제품·2026.07 ~ · 마켓플레이스 발행·기획·개발 단독

Claude Usage Crab

서브에이전트가 지금 뭘 하는지 보여주는 확장.

JavaScriptVS Code Extension APINode.jsdiagnostics_channelOAuth PKCE

설치 · VS Code 마켓플레이스 · 소스 · github.com/gnldnd11/claude-usage-monitor

Claude Usage Crab는?

Claude Code의 서브에이전트가 지금 무엇을 하고 있는지를 VS Code 사이드바에서 보여주는 확장이다. .claude/agents/의 에이전트 하나하나가 픽셀 캐릭터가 되고, 실제로 호출되면 방으로 걸어 들어와 책상에 앉아 일한다. 같은 패널에 세션(5시간)·주간(7일)·컨텍스트 사용량이 함께 있다.

VS Code 마켓플레이스에 saegim.claude-usage-crab으로 발행했다. 읽기 전용이고, 훅을 걸지 않고, 설정할 것이 없다.

호출된 에이전트가 방에 들어와 책상에서 일한다. 아래는 전체 로스터.
호출된 에이전트가 방에 들어와 책상에서 일한다. 아래는 전체 로스터.

Anthropic과 무관한 비공식 커뮤니티 확장이다.

왜 만들었나

백그라운드 서브에이전트는 블랙박스다. 던져놓고 나면 지금 일하는 중인지, 끝난 건지, 처박힌 건지 알 수가 없다.

이건 내 문제였다. 이 모노레포 자체가 에이전트 여러 종(계획·구현·리뷰·커밋·검증·사서)으로 굴러간다. 몇 개를 병렬로 던져놓고 "쟤 아직 도는 중인가"를 로그로 뒤지는 게 일상이었다. 그 상태를 한눈에 답해주는 화면이 필요했다.

시작은 사용량 미터였다. 에이전트 탭은 나중에 붙은 축인데, 지금은 이쪽이 제품의 얼굴이 됐다. 만들면서 문제가 더 선명해진 경우다.

전체 구조

핵심 제약은 관측 대상이 협조해주지 않는다는 것이다. Claude Code는 이 확장을 위한 API를 열어주지 않는다. 그래서 이미 디스크에 쓰이고 있는 것(트랜스크립트)과 이미 네트워크로 오가는 것(사용량 응답)만 읽는다. 훅이 없다는 건 곧 에이전트 실행을 방해할 방법이 구조적으로 없다는 뜻이기도 하다.

협조해주지 않는 대상을 관측하는 구조. 두 갈래로 들어와 상태 판정에서 합쳐진다.
협조해주지 않는 대상을 관측하는 구조. 두 갈래로 들어와 상태 판정에서 합쳐진다.
0
추가 API 요청 (정상 사용 시)
0
훅 · 설정 · 빌드 스텝
2.7K
줄 JavaScript
38
캐릭터 스프라이트
12
방 테마
1인
기획~발행 단독
서브에이전트 실시간 관측 (읽기 전용 · 훅 없음)
에이전트별 토큰·도구호출·소요시간 귀속
처박힌 에이전트 탐지 (자기 중앙값 3배)
세션 · 주간 · 컨텍스트 사용량 미터
증분 트랜스크립트 파싱 (수백 MB 대응)
이름 해시 기반 결정적 캐릭터 배정

남을 방해하지 않고 남을 관측하기

이 확장에서 제일 마음에 드는 부분이다.

세션·주간 한도는 /api/oauth/usage에서 온다. 그런데 이 엔드포인트는 토큰당 몇 분에 한 번만 통과시킨다. Claude Code가 쓰고 있는 동안 같은 토큰으로 부르면 429가 난다. 폴링을 촘촘하게 할수록 조용한 실패만 쌓인다.

그래서 부르는 대신 듣기로 했다. Claude Code의 확장은 같은 extension host 프로세스에서 돈다. Node의 diagnostics_channel을 구독하면 그쪽이 자기 사용량을 조회하는 요청이 그대로 보인다. 응답 스트림을 통과시키면서 사본만 뜨면, 내 요청은 한 번도 나가지 않는다.

// 응답 스트림을 소비하면 Claude Code가 자기 응답을 못 받는다.
// 리스너를 가로채 원본에 청크를 그대로 넘기고, 사본만 모은다.
res.on = function (event, listener) {
  if (event === 'data') {
    return origOn('data', (chunk) => {
      if (body.length < MAX) body += chunk.toString();
      listener(chunk);            // ← 남의 것은 건드리지 않는다
    });
  }
  ...
};

그래서 패널의 숫자가 Claude Code 내장 다이얼로그와 정확히 일치한다. 계산이 같아서가 아니라 같은 응답이기 때문이다.

세션·주간·Fable·컨텍스트. 이 숫자들은 내가 부른 게 아니라 옆에서 들은 것이다.
세션·주간·Fable·컨텍스트. 이 숫자들은 내가 부른 게 아니라 옆에서 들은 것이다.

여기엔 함정이 둘 있었다. 폴백으로 내가 직접 부른 요청이 내 탭에 다시 잡히면 무한루프가 된다. 자기 요청에는 전용 헤더를 박아 걸러낸다. 그리고 이 코드는 남의 확장이 도는 프로세스 안에서 실행되므로, 탭 핸들러 전 구간을 catch로 감쌌다. 내가 터져도 Claude Code는 멀쩡해야 한다.

끝난 걸 끝났다고 말하기

가장 오래 붙잡은 버그다. 서브에이전트가 끝났는지를 트랜스크립트만 보고 알아내야 한다.

동기 호출은 쉽다. tool_result가 오면 끝난 거다. 문제는 백그라운드 에이전트인데, 이쪽은 tool_result를 주지 않는다. 던지는 즉시 "실행했음" 확인만 돌려주고, 진짜 완료는 한참 뒤에 별도 알림으로 온다.

이걸 모르고 확인 응답을 완료로 셌더니, 백그라운드 에이전트가 던지자마자 끝난 것으로 표시됐다. 이 탭이 존재하는 이유가 정확히 그 케이스인데 말이다. 함정은 세 겹이었다.

  • 즉시 오는 확인 응답은 완료가 아니다. 명시적으로 걸러내야 한다.
  • 완료 알림은 같은 내용이 최대 세 번, 서로 다른 레코드 형태로 쓰인다. 비용 정보를 달고 온 놈을 골라 남긴다.
  • 백그라운드는 확인 응답이 호출 2초 뒤에 떨어지므로 (결과시각 − 호출시각)은 소요시간이 아니다. 하네스가 찍어준 값만 소요시간으로 인정한다.

세 번째가 특히 중요하다. 소요시간을 잘못 계산하면 그 위에 얹은 것이 전부 무너진다. 처박힘 판정도, 에이전트별 비용도.

처박힌 걸 알려주는 기준

에이전트가 오래 도는 건 정상일 수도 있고 아닐 수도 있다. 절대 시간으로는 판정할 수 없다. 그래서 자기 자신의 중앙값 소요시간 3배를 넘으면 호박색으로 바꾼다. 빠른 에이전트가 첫 느린 실행에서 오탐되지 않게 10분 아래로는 절대 처박혔다고 부르지 않고, 한 시간 넘게 결과가 없으면 죽은 세션으로 보고 내린다.

반대 방향의 보정도 필요했다. 백그라운드는 확인 응답이 2초 만에 오기 때문에, 그대로 두면 캐릭터가 방에 들어오기도 전에 사라진다. 사람 눈에 보이라고 만든 화면이 사람 눈에 안 보이는 셈이다. 그래서 최소 6초는 "일하는 중"으로 유지한다.

수백 MB를 10초마다 읽지 않기

트랜스크립트는 append-only고, 무거운 세션은 수백 MB까지 간다. 10초마다 전량 재파싱은 성립하지 않는다.

파일마다 누적기를 두고 마지막 틱 이후 늘어난 바이트만 offset으로 읽는다. 잘린 마지막 줄은 다음 틱으로 넘긴다. 전량 재파싱은 파일이 줄었을 때(재작성)와 날짜가 바뀌었을 때(오늘 합계가 이동)만 한다. 정상 틱이 150ms대에서 수 ms로 내려갔다.

여기서 재밌었던 건 burn-rate 경고다. "마지막으로 본 시점보다 새로운 요청 중 최대값"을 알아야 하는데, 그 기준 시점은 파싱 이후에 움직이고 지나간 줄은 두 번 다시 읽지 않는다. 그래서 파일마다 스카이라인(뒤에 더 큰 값이 나오면 앞을 버리는 단조 스택)을 유지한다. 나중에 어떤 임계값으로 물어도 파일을 다시 열지 않고 답한다.

컨텍스트 게이지에도 비슷한 판단이 하나 있다. 컨텍스트 창이 200K인지 1M인지는 메시지가 아니라 세션의 속성이다. 마지막 메시지만 보면, 1M 세션이 압축으로 200K 아래로 내려간 순간 "200K 창이 거의 찼다"로 오독된다. 그래서 그 세션의 최대 컨텍스트로 티어를 판정한다.

호출을 몸으로 보여주기

로그 한 줄로 "agent started"라고 쓰면 읽어야 알고, 읽으려면 찾아가야 한다. 그래서 방을 만들었다.

워커는 에이전트 이름이 아니라 호출 단위로 만든다. 같은 에이전트를 병렬로 세 번 부르면 캐릭터 세 명이 나란히 선다. 이름이 같다고 하나로 접으면 병렬로 던졌다는 사실 자체가 화면에서 사라진다.

캐릭터 배정은 이름 해시로 결정적이다. 같은 에이전트는 영원히 같은 얼굴이라 한 번 익히면 계속 통한다. 다만 해시를 그대로 쓰면 둘이 같은 캐릭터를 뽑는 일이 생겨서, 로스터 전체를 놓고 충돌 없이 배정한다. 사용자가 직접 고른 외형이 먼저 자리를 잡고, 나머지가 빈 자리를 채운다.

캐릭터에 커서를 올리면 크루 카드. 레벨·XP·누적 작업량이 전부 실측이다.
캐릭터에 커서를 올리면 크루 카드. 레벨·XP·누적 작업량이 전부 실측이다.

XP는 완료한 일만큼 쌓인다. 여기서 하나 정한 게 있다. 활동 시간은 프로필에 보여주되 XP에는 넣지 않는다. 시간은 비용이지 업적이 아니라서다. 오래 돈 에이전트가 레벨이 높아지면 레벨이 거짓말을 하게 된다.

설치 첫날 모두가 Lv.1로 시작하는 것도 같은 종류의 거짓말이라, 최초 1회 과거 트랜스크립트를 훑어 레벨을 복원한다. 이건 무거운 작업이라 JSON 파싱 없이 정규식만 쓰고, 40ms 일하고 150ms 쉬는 식으로 잘라서 돌린다. 확장 호스트를 막으면 에디터 전체가 버벅인다.

방 하나 그리는 데 규칙이 필요했다

방 테마는 지금 12종이다. 처음엔 정사각 디오라마를 통째로 넣었는데 세 가지가 동시에 터졌다. 라이트 테마에서 검은 사각형이 스티커처럼 박히고, 800px 그림이 280px 사이드바에서 뭉개지고, 세로를 40% 잡아먹었다.

원인은 하나였다. 디오라마를 하나의 물체로 보여준 것. 방 안쪽으로 파고들어 자르면 프레임이 바닥과 벽으로 꽉 차서 배경 자체가 사라지고, 셋이 한 번에 해결된다. 지금은 1.8:1 실내 비네트로 잘라 쓰고, 절차를 문서로 못 박아뒀다.

이전 · 정사각 디오라마를 통째로
이전 · 정사각 디오라마를 통째로
이후 · 방 안쪽으로 파고들어 자른 비네트
이후 · 방 안쪽으로 파고들어 자른 비네트

같은 방(Backrooms)을 같은 그림으로 찍은 전후다. 그림을 새로 그린 게 아니라 어디를 자르느냐만 바꿨다.

Decision #01배경 투명화는 하지 않는다

투명 처리로 라이트 테마 문제를 풀려고 floodfill, connected-components, 모폴로지까지 전부 시도했고 전부 실패했다. 이 그림들은 어두운 외곽선이 배경과 톤이 이어져 있어서, 색 기반 제거가 그림 내부까지 타고 들어가 캐릭터와 가구에 구멍을 낸다. 크롭이 정답이다.

Decision #02정지 픽셀아트 위에 블러 레이어를 얹지 않는다

모니터 화면에 글로우를 주려고 radial-gradient + blur + screen 블렌드를 얹었더니 안개 낀 것처럼 됐다. 픽셀아트는 경계가 또렷한 게 매력인데 블러가 그걸 깎는다. 접근 자체가 안 맞았다.

방이 12종이 되고 나서야 다른 문제가 보였다. 고르는 자리가 드롭다운이라 한 번에 이름 하나만 보였고, 그래서 대부분은 방이 여러 개라는 것 자체를 모르고 썼다. 열두 개를 만들어놓고 하나만 보여준 셈이다. 지금은 눌렀을 때 썸네일 그리드가 열리고, 지금 쓰는 방이 강조돼 있다.

여기에도 함정이 하나 있었다. 고르라고 원본 방 그림 열두 장을 한꺼번에 띄우면 7MB를 디코딩하느라 웹뷰가 멈춘다. 그래서 방마다 200px에 128색으로 줄인 썸네일을 따로 굽는다. 열두 장 합쳐 148KB다.

아무도 안 돌 때 방이 허전한 문제는 아직 미해결이다. 유휴 시 마스코트를 책상에 앉히려 했지만 뒷모습 스프라이트가 없어서 접었다. 다시 손댄다면 블러나 반투명이 아니라 또렷한 스프라이트를 하나 더 놓는 방향이어야 한다.

라이브로 보려면, 열려 있지 않은 문을 지나야 한다

세션 %를 진짜 라이브로 보여주려면 자기 토큰이 필요하다. 이 확장에서 유일하게 깔끔하지 않은 부분이고, 지금도 답을 기다리는 중이라 여기 그대로 적는다.

/api/oauth/usage의 rate limit은 계정이 아니라 토큰별이다. Claude Code의 토큰을 빌려 쓰면 그 창을 Claude Code와 나눠 갖게 되고, 활성 사용 중에는 거의 항상 429가 난다. 폴 간격과 재시도를 열두 번 손봐도 못 이겼다. 갱신이 리로드 시점에만 일어나면 그건 라이브가 아니다.

자기 토큰이면 자기 예산이라 60초 폴링이 매번 통과한다. 그래서 선택형 사인인(PKCE OAuth)을 넣었고, 그게 세션 %가 살아 움직이는 유일한 길이었다.

문제는 이게 서드파티에 열려 있는 길이 아니라는 것이다. 확장 전용 클라이언트를 등록할 창구가 없어서 Claude Code의 클라이언트로 붙고 있고, 정책은 이 방식의 OAuth를 자사 제품(Claude Code·Claude.ai) 전용으로 제한한다. 읽어보고 회색지대라고 판단하지 않았다. 제한에 걸린다고 봤다. 차단이 진행 중이라는 공개 이슈도 있으니 언제 막혀도 이상하지 않은 상태다.

여기서 선택지는 셋이었다. 모른 척 쓰거나, 조용히 내리거나, 물어보거나.

Decision #03숨기는 대신 직접 물었다 (2026-07-24 문의, 답변 대기)

Anthropic 지원팀에 세 가지를 계단식으로 물었다. ① 서드파티가 개인 플랜 사용량을 조회할 공식 방법이 있는가 ② 없다면 429 없이 갱신할 권장 방법이 있는가 ③ 둘 다 없고 이 방식이 안 되는 거라면 즉시 내리겠다.

1차 답변으로 확인된 건 공개 API로는 서드파티가 개인 플랜 사용량을 조회할 방법이 없다는 것이다(공개된 Usage/Cost API는 조직의 API 소비량을 보는 것이지 플랜 한도가 아니다). 정책 적합성 판단은 담당자에게 넘어갔고, 지금은 그 답을 기다리는 중이다.

그동안 손 놓고 기다리지는 않았다. 제품의 무게중심을 로그인 없이 도는 것으로 옮겼다. 에이전트 관측·토큰·요청·컨텍스트는 전부 로컬 트랜스크립트에서 나오고, 사인인은 세션 % 하나를 라이브로 만드는 부가 기능이다. 답이 "안 된다"로 오면 그 기능만 걷어내면 되고, 확장은 그대로 선다.

되는 걸 찾아내는 것과, 되니까 그냥 쓰는 건 다르다고 생각한다.

프라이버시와 경계

모든 데이터는 로컬 ~/.claude에서 읽는다. 텔레메트리도, 중계 서버도 없다. 토큰은 api.anthropic.com 외에는 어디로도 나가지 않는다. 자체 로그인은 선택 사항이고(자기 토큰이면 자기 예산이라 세션 %가 더 자주 갱신된다), PKCE OAuth로 받아 VS Code secrets에 넣는다.

빌드 스텝이 없다는 게 여기서 보안 논거가 된다. 트랜스파일도 번들러도 없는 순수 JavaScript라 .vsix에 들어간 게 레포에 있는 그것이다. 감사하려는 사람이 대조할 수 있다.

에이전트 숨기기는 표시 전용이다. 숨겨도 그 에이전트는 평소대로 돈다. 관측 도구가 관측 대상을 바꾸기 시작하면 그때부터는 다른 물건이다.

기술 스택

  • 런타임: VS Code Extension API (^1.75) · Node.js · 순수 JavaScript (빌드 없음)
  • 관측: diagnostics_channel · 증분 파일 읽기 (fs.readSync offset) · fs.watch
  • 인증: PKCE OAuth (선택) · VS Code SecretStorage
  • 화면: 웹뷰 (스프라이트 시트 크롭 렌더) · 상태바 아이템
  • 배포: VS Code 마켓플레이스 (saegim.claude-usage-crab) · 공개 미러 레포

왜 이 스택

  • 빌드 없는 순수 JS: 확장이 남의 토큰을 다루니까, 배포물과 소스가 1:1로 대조되는 게 기능보다 중요했다.
  • diagnostics_channel: 유일하게 "부르지 않고 아는" 길이었다. 폴링은 429로 조용히 실패하고, 훅은 사용자 설정을 요구한다.
  • 웹뷰 + 스프라이트 시트: 구매한 시트마다 셀 크기(32/48/64px)와 레이아웃이 달라서, 시트별 메타와 실측 bbox로 크롭해 어떤 캐릭터든 화면에서 같은 키로 보이게 정규화했다.

정직한 한계

읽고 있는 것 두 가지가 모두 문서화되지 않은 것이다. 사용량 엔드포인트도, 트랜스크립트 포맷도. 바뀌면 해당 숫자가 멈춘다. 이건 확장 소개에도 그대로 적어뒀다. 감출 성질의 것이 아니라 쓰는 사람이 알고 써야 하는 사실이다.

백그라운드 서브에이전트는 알 수 있는 진행률이 없다. 그래서 퍼센트를 지어내는 대신 불확정 바와 경과시간만 보여준다. 화면이 덜 그럴듯해지는 대신 거짓말을 하지 않는다.

자체 사인인은 정책 제한에 걸린다고 보고 직접 물어둔 상태다. 답에 따라 기능이 빠질 수 있고, 그 전제로 제품 구조를 짜뒀다.

에이전트 탭은 아직 Beta다. 그리고 번들된 픽셀아트는 내가 만든 게 아니다. 코드만 MIT이고 아트는 이 확장 안에서만 쓰도록 라이선스된 서드파티 자산이다.

회고

이 프로젝트의 값은 사용량 미터를 하나 더 만든 데 있지 않다.

협조하지 않는 대상을 관측하는 방법을 찾아낸 것이 하나다. API도 훅도 없이, 남이 디스크에 쓰는 것과 남이 네트워크로 주고받는 것만으로. 그러면서 그 남을 한 번도 방해하지 않는다. 스트림은 통과시키고, 내 요청은 배제하고, 전 구간을 catch로 감쌌다.

내가 매일 겪는 문제를 내 도구로 해결하고 발행까지 한 것이 다른 하나다. 에이전트로 일하다가 "얘가 지금 도는 중인가"에 막혔고, 그걸 화면으로 만들었고, 마켓플레이스에 올렸다. 쓰는 사람이 곧 만든 사람이라 매일 회귀 테스트가 돌아간다.

그 아래를 받치는 건 결국 평범한 것들이다. 수백 MB를 감당하는 증분 파싱, 세 겹 함정을 통과한 완료 판정, 지어낸 퍼센트를 거부한 판단.