tgrep·CodeGraph·Mantic·ripwire는 ripgrep을 대신할 수 있나
에이전트에게 코드베이스를 뒤지게 하면 대부분 rg를 부릅니다. 인덱스를 붙여 이걸 빠르게 하거나, 아예 다른 방식으로 답을 찾겠다는 도구가 몇 개 나와 있습니다. 네 개를 골라 실제 저장소에서 ripgrep과 나란히 재봤는데, 도구마다 성패를 가르는 변수가 서로 다른 종류였습니다.
- tgrep의 배속은 저장소 크기와 무관합니다. kubernetes 한 저장소 안에 45.13배와 0.33배가 함께 있습니다
- CodeGraph는 이분법입니다. 언어 커버리지 64% 이상인 저장소 다섯은 전부 만점, 24%인 둘은 절반 이하입니다. 중간이 없습니다
- Mantic은 파일 내용을 읽지 않습니다. 마케팅 문구가 아니라 설계 그대로입니다
- ripwire는 어법 하나로 1위가 1건에서 10건으로 바뀝니다. 같은 과제를 자연어로 묻느냐 심볼명으로 묻느냐의 차이입니다
대상은 tgrep(트라이그램 인덱스 regex grep), CodeGraph(심볼·호출 그래프 인덱스), Mantic(경로 기반 의도 검색), ripwire(데몬 없는 심볼 그래프)이고 기준선은 ripgrep입니다.
무엇을 어떻게 쟀나
두 대의 머신에서 서로 모르는 채 독립적으로 측정한 뒤 합쳤고, 규모 공백을 메우려 공개 저장소 셋을 추가로 돌렸습니다.
| 저장소 | 파일 수 | |
|---|---|---|
| 측정 A | 개인 저장소 3개 (Go+TS, SQL 중심 C, Python) | 330 ~ 973 |
| 측정 B | 사내 플러그인 저장소, 리눅스 커널 | 4,076 / 95,916 |
| 측정 C | vllm, litellm, kubernetes | 6,963 / 10,628 / 31,261 |
| 측정 D | 같은 셋에 ripwire 추가 | 6,963 / 10,628 / 31,261 |
합쳐서 저장소 8개, 정규식 쿼리 48셀, 과제 40건입니다. 과제는 에이전트가 던질 법한 질문을 주고 정답 파일에 도달하는지 보는 방식이고, 정답은 ripgrep과 코드 읽기로 사람이 확정했습니다.
속도를 믿기 전에 결과 집합이 같은지부터 봤습니다. 앞선 30셀은 매치 줄 수가 전부 일치했습니다.
넷은 다른 질문에 답합니다
속도로 나란히 세우는 것 자체가 범주 오류입니다. Mantic의 README도 "Quick Text Searches → Use ripgrep"이라고 스스로 적어놨습니다.
| tgrep | CodeGraph | Mantic | ripwire | |
|---|---|---|---|---|
| 질문 | "이 정규식에 맞는 줄은" | "이 심볼 바꾸면 뭐가 깨지나" | "어느 파일부터 볼까" | "이 일에 필요한 맥락은" |
| 색인 대상 | 파일 내용의 트라이그램 | 파일 심볼과 호출 관계 | 파일 경로와 메타데이터 | 파일 심볼과 본문 (캐시) |
| 반환물 | 매치된 줄 (rg와 동일) | 심볼·호출 경로·영향 범위 | 순위 매긴 파일 경로 | 순위 매긴 심볼·구간 |
| 상주 프로세스 | tgrep serve |
codegraph serve --mcp |
없음 | 없음 |
| ripgrep과의 관계 | 대체재 | 보완재 | 보완재 | 보완재 |
트라이그램 인덱스는 파일 내용을 세 글자씩 잘라 미리 색인해두고, 쿼리에서 리터럴을 뽑아 그 조각을 가진 파일만 후보로 좁히는 방식입니다. tgrep이 빨라지는 것도, 느려지는 것도 전부 여기서 나옵니다.
tgrep — 저장소가 아니라 쿼리가 결정합니다
같은 저장소에 선택도가 다른 쿼리 6단계를 순서대로 던졌습니다. 위로 갈수록 매치가 적고 리터럴이 뚜렷한 쿼리입니다.
| 쿼리 단계 | 사내 4k | vllm 7k | litellm 10k | kubernetes 31k | linux 96k | 중앙 |
|---|---|---|---|---|---|---|
| 1 희귀 리터럴 | 12.56x | 12.56x | 13.10x | 45.13x | 206.43x | 13.10x |
2 -l 파일목록 |
11.73x | 9.81x | 3.42x | 4.62x | 62.28x | 9.81x |
| 3 흔한 리터럴 | 9.54x | 2.37x | 3.18x | 1.55x | 10.18x | 3.18x |
| 4 대소문자 무시 | 4.25x | 1.03x | 0.66x | 1.21x | 8.28x | 1.21x |
| 5 앵커 정규식 | 7.67x | 0.45x | 0.51x | 0.65x | 1.83x | 0.65x |
| 6 리터럴 없음 | 0.98x | 0.25x | 0.25x | 0.33x | 0.98x | 0.33x |
세로로 읽으면 다섯 저장소 중 넷에서 완벽히 단조 감소합니다. 쿼리 종류만 알면 배속 순위가 정해집니다. 예외는 사내 저장소 한 열이고, 앵커 정규식이 대소문자 무시보다 높은 한 곳이 뒤집혀 있습니다.
가로로 읽으면 규칙이 없습니다. 저장소 크기 순으로 중앙값을 늘어놓으면 8.61 · 1.70 · 1.92 · 1.38 · 9.23배로 오르내립니다. kubernetes 한 저장소 안에 45.13배와 0.33배가 함께 있습니다. 크기는 변수가 아닙니다.
기제는 둘입니다. 인덱스는 후보 파일을 좁혀 스캔을 아끼는 장치인데, 추출할 리터럴이 없으면 좁히지 못하고(6단계), 매치가 많으면 좁혀도 전달 비용이 그대로 남습니다(3~5단계). 둘이 겹치면 비기는 게 아니라 4배까지 집니다.
앞선 두 측정에서 tgrep의 최악은 0.84배로 비김에 가까웠는데, 대형 저장소를 넣자 0.25배가 나왔습니다. 차이는 매치 수입니다. 리눅스의 [A-Z]{20,}는 2,143줄만 반환했지만 여기 [A-Z]{8,}는 19,057~81,579줄을 반환합니다.
"최대 52배"는 재현되지 않았습니다
같은 리눅스 커널 안에서 쿼리 묶음만 바꿔도 집계 배수가 이렇게 움직입니다.
| 쿼리 묶음 | 집계 배수 |
|---|---|
선택적 쿼리만 (희귀 리터럴 + -l) |
90.9x |
| 6개 전부 | 3.4x |
| 적대적 쿼리만 (24만 매치 + 리터럴 없음) | 1.28x |
그리고 4,076파일 저장소와 95,916파일 리눅스의 집계 배수가 3.42배와 3.36배로 거의 같았습니다. 파일 수가 20배 달라도 쿼리 구성이 같으면 배수가 같습니다.
손익분기와 인덱스 비저장
인덱스를 만드는 비용을 쿼리로 갚아야 합니다.
| 저장소 | 파일 | 인덱스 구축 | 쿼리당 절약 | 손익분기 |
|---|---|---|---|---|
| linux | 95,916 | 11.8 s | 1,643 ms | 7.2 쿼리 |
| 사내 플러그인 | 4,076 | 0.84 s | 66 ms | 12.7 쿼리 |
| 개인 저장소 (Go+TS) | 873 | 140 ms | 7.6 ms | 18 쿼리 |
| 개인 저장소 (Python) | 973 | 280 ms | 8.1 ms | 35 쿼리 |
저장소가 작을수록 손익분기가 멀어집니다. ripgrep이 이미 빠르기 때문입니다.
더 곤란한 건 인덱스가 없을 때입니다. 리눅스에서 쟀습니다.
| 상태 | 지연 |
|---|---|
| 서버 가동 중 | 0.01 s |
| 서버 중지, 디스크 인덱스 존재 | 0.02 s |
| 인덱스 없음 | 16.3 s |
| ripgrep (참조) | 1.75 s |
인덱스가 없으면 ripgrep의 9.3배이고, 그 16초짜리 빌드는 디스크에 저장되지 않습니다. 2회차도 15.9초였고 인덱스 디렉터리가 생기지 않았습니다. 소형 저장소에서도 .tgrep이 생성되지 않는 것을 확인했습니다. 세션마다 새 저장소를 건드리는 에이전트에게는 이게 기본 경로입니다.
드롭인은 아닙니다
48셀 중 46셀에서 ripgrep과 tgrep은 같은 답을 냈습니다. kubernetes에서 처음 갈렸습니다.
| 셀 | ripgrep | tgrep | 차 |
|---|---|---|---|
대소문자 무시 namespace |
76,458줄 | 75,757줄 | 701 |
리터럴 없음 [A-Z]{8,} |
81,579줄 | 81,371줄 | 208 |
원인은 단 하나의 파일, 3.3MB짜리 생성된 protobuf였습니다. tgrep이 색인 단계에서 내용 검사로 이진 판정해 제외했고 ripgrep은 여기서 1,156줄을 매치로 내놨습니다. 검색 정확성이 아니라 이진 파일 판정 기준의 차이입니다. 소스를 찾는 목적이라면 빼는 쪽이 낫지만, 의미론적 드롭인은 아니라는 건 알고 써야 합니다.
CodeGraph — 언어 커버리지가 전부입니다
| 저장소 | 주력 언어 | 텍스트 파일 | 색인 | 커버리지 | 과제 정답 |
|---|---|---|---|---|---|
| litellm | Python + TSX | 10,031 | 8,489 | 85% | 5/5 |
| 개인 저장소 | Python | 973 | 827 | 85% | 5/5 |
| 개인 저장소 | Go + TS | 873 | 745 | 85% | 5/5 |
| vllm | Python | 6,577 | 5,474 | 83% | 5/5 |
| kubernetes | Go + YAML | 30,933 | 19,834 | 64% | 5/5 |
| 개인 저장소 | SQL 최다 | 330 | 80 | 24% | 3/5 |
| 사내 플러그인 | Markdown 최다 | 4,076 | 992 | 24% | 3/10 |
중간이 없습니다. 커버리지 64% 이상인 다섯은 전부 만점이고 24%인 둘은 절반 이하입니다. 품질이 서서히 나빠지는 게 아니라 주력 언어가 지원 목록에 있느냐 없느냐입니다. Markdown과 SQL은 목록에 없습니다.
지원 목록에 있을 때는 압도적입니다. vllm·litellm·kubernetes의 15과제를 전부 1위로 맞혔고 출력은 중앙값 484 토큰이었습니다. 정의 위치를 정확히 짚는 유일한 도구입니다.
| 심볼 | CodeGraph | 실제 |
|---|---|---|
NewCloudNodeController |
node_controller.go:117 |
:117 |
NewHorizontalController |
horizontal.go:138 |
:138 |
LeaderElector |
leaderelection.go:188 |
일치 |
앞선 두 측정에서 CodeGraph가 25과제 중 16건에 그친 이유가 이걸로 분명해집니다. 놓친 건 전부 지원하지 않는 언어였습니다. Markdown 2,262개, SQL 64개입니다.
대신 색인 비용이 규모와 함께 벌어집니다.
| 저장소 | tgrep | CodeGraph |
|---|---|---|
| vllm 6,963 | 0.86 s · 70 MB | 17.5 s · 377 MB |
| litellm 10,628 | 1.86 s · 136 MB | 27.3 s · 633 MB |
| kubernetes 31,261 | 2.85 s · 220 MB | 60.4 s · 992 MB |
kubernetes에서 tgrep보다 21배 오래 걸리고 인덱스가 4.5배 큽니다. 그러면서 63%만 봅니다. 리눅스 커널(95,916 파일)에서는 codegraph init이 20분 뒤에도 완주하지 못했습니다. 다만 이건 1회 관측이고 백그라운드 실행이 방해받았을 가능성을 배제하지 못했습니다.
Mantic — 파일 이름이 답을 말해주는 저장소에서만
Mantic은 파일 내용을 읽지 않습니다. README의 "infers intent from file structure and metadata rather than brute-force reading content"는 설계 그대로입니다. PG_FUNCTION_ARGS는 C 파일 3개에 들어 있는데 Mantic은 아무것도 반환하지 않았고, 경로에 그 글자가 있는 sql은 정확히 반환했습니다.
| 저장소 | 정답 도달 | 성격 |
|---|---|---|
| vllm · litellm · kubernetes | 15/15 | router.py, budget_manager.py, kubelet.go — 이름이 곧 답 |
| 개인 저장소 (Go+TS) | 4/5 | 서술적 파일명 |
| 사내 플러그인 | 5/10 (+오답 1) | Markdown 다수 |
| 개인 저장소 (SQL 중심 C) | 1/5 | 파일명이 기능을 덜 말함 |
| 개인 저장소 (Python) | 0/5 | 중첩 git 저장소 |
"도달"은 결과 어딘가에 정답 파일이 있다는 뜻이지 1위로 올렸다는 뜻이 아닙니다. 대형 저장소 15과제에서 도달은 15건이지만 1위는 5건, 상위 5위는 10건입니다. 같은 15과제를 전부 1위로 맞힌 CodeGraph와는 다른 축의 숫자입니다.
마지막 줄이 별도의 문제입니다. 그 저장소에는 .gitmodules 없이 자체 .git을 가진 중첩 저장소가 있었습니다.
| 도구 | 반환 경로 | 중첩 저장소 하위 |
|---|---|---|
| ripgrep · tgrep | 391 | 372 |
| CodeGraph | 188 | 181 |
| Mantic | 27 | 0 |
질의를 바꿔도 하나도 반환하지 않습니다. 추적 파일 1,046개 중 1,007개, 96%가 보이지 않습니다. git ls-files가 중첩 저장소를 단일 gitlink 항목 하나로 반환하고 Mantic이 그 안으로 내려가지 않기 때문입니다. --path로 직접 가리키면 동작하니 기본 탐색만 실패하는데, 조용히 실패합니다.
그리고 규모에 따라 비용이 뒤집힙니다.
| 소형 저장소 | 대형 저장소 | |
|---|---|---|
| 반환 파일 | 2 ~ 6개 | 항상 정확히 100개 |
| 출력 토큰 중앙값 | 12 | 1,038 |
| 15과제 출력 합계 | — | 72,549 B |
kubelet, scheduler, leader 어느 질의를 던져도 정확히 100개가 돌아옵니다. 하드 상한입니다. 소형에서 253바이트로 가장 쌌던 도구가 대형에서는 CodeGraph(26,660 B)의 2.7배를 씁니다. 정답은 그 100개 안에 있지만 중앙 순위가 5위여서 위에서부터 읽어야 합니다.
낡는 방식이 다릅니다
인덱스를 두는 도구의 진짜 위험은 속도가 아니라 여기 있습니다. 픽스처 저장소로 네 시나리오를 쟀습니다. STALE-MISS는 새로 쓴 코드를 못 보는 것이고, STALE-PATH는 존재하지 않는 경로를 답으로 주는 것입니다.
| 도구 | 파일 수정 | 파일 추가 | 파일 삭제 | 파일 이동 | 감시 프로세스 |
|---|---|---|---|---|---|
| ripgrep | fresh | fresh | fresh | fresh | 불필요 |
| ripwire | fresh | fresh | fresh | fresh | 불필요 |
| tgrep serve | fresh | fresh | fresh | fresh | 필요 |
| CodeGraph (데몬) | fresh | fresh | fresh | fresh | 필요 |
| tgrep (CLI 단독) | STALE-MISS | STALE-MISS | fresh | STALE-MISS | — |
| CodeGraph (데몬 없음) | STALE-MISS | STALE-MISS | STALE-PATH | STALE-PATH | — |
| Mantic (git 저장소) | fresh | fresh | STALE-PATH | STALE-PATH | — |
ripwire는 아래에서 따로 다룹니다. 나머지 넷부터 보겠습니다.
tgrep은 안전하게 실패합니다. 새 코드를 못 볼 뿐 없는 경로를 지어내지 않습니다. 인덱스로 후보를 좁힌 뒤 질의 시점에 실제로 파일을 읽기 때문입니다. 파일이 없으면 매치도 없습니다.
CodeGraph는 위험하게 실패합니다. 데몬 없이 부르면 삭제한 파일과 이동 전 경로를 그대로 답으로 줍니다. 그리고 codegraph status는 낡았다고 말해주지 않습니다. 전 측정에서 stale 경고가 한 번도 나오지 않았습니다. 에이전트에게는 빈손보다 나쁩니다. 빈손이면 다른 방법을 찾지만 오답이면 그걸 읽으러 갑니다.
두 도구 모두 감시 프로세스가 붙어 있을 때만 완전하고, 그 감시자는 CLI에 없습니다. tgrep은 tgrep serve, CodeGraph는 codegraph serve --mcp 안에 있습니다. 전파 지연은 tgrep이 283ms~7,105ms, CodeGraph가 0.7~2.0초였습니다. 에이전트는 보통 MCP로 붙으니 감시 모드가 기본이지만, 스크립트나 훅에서 CLI로 부르면 다른 세계입니다.
Mantic은 두 측정이 정반대로 나와서 파일 3개짜리 최소 재현으로 갈랐습니다.
| 조건 | beta 삭제 후 결과 |
|---|---|
| git 저장소 | alpha, beta, gamma — 유령 |
git 저장소, git add -A 후 |
alpha, gamma — 정상 |
| git 저장소가 아닌 디렉터리 | alpha, gamma — 정상 |
git 저장소 안에서는 git의 파일 목록을 쓰고 디스크 존재를 확인하지 않습니다. 추가는 즉시 반영되고 삭제는 스테이징 전까지 남습니다. 편집 후 검색에서 추가만 시험하면 "항상 정확"으로 보입니다.
순위가 높다는 것과 답이 맞다는 것은 다릅니다
claude-opus-5 모델 DB를 묻는 과제에서 Mantic과 CodeGraph 둘 다 그럴듯한 이름의 파일을 상위로 올렸는데, 검증하니 그 파일에 claude-opus-5는 0번 나왔습니다. 정답은 다른 경로의 같은 이름 파일이었습니다. 정답을 내용으로 검증하는 절차가 없었다면 그대로 정답으로 집계됐을 겁니다.
운영 위험 — 데몬이 죽지 않습니다
픽스처 디렉터리를 통째로 지우고 다시 만들었는데 CodeGraph 데몬이 3분 넘게 살아서 사라진 디렉터리의 옛 데이터베이스로 계속 응답했습니다. 그래서 재생성된 저장소에 대한 질의가 전부 틀렸습니다.
codegraph daemon은 목록에서 하나 골라 엔터를 누르는 대화형 선택기입니다. --stop-all 같은 플래그가 없어 스크립트에서 끌 수 없고 pid를 직접 죽여야 합니다. stale을 관리하는 도구가 stale의 원인이 되는 구조입니다.
설치 잔재도 한 번 물렸습니다. 세 저장소 모두 .codegraph가 이전 설치의 깨진 심볼릭 링크였는데, codegraph status는 "Not initialized, run codegraph init"이라고만 하고 init은 ENOENT ... mkdir로 죽습니다. 원인을 알 수 없는 무한 루프이고 깨진 링크를 손으로 지워야 풀립니다.
같은 형태가 실제 배포에서 터졌습니다
code-yeongyu/oh-my-openagent가 CodeGraph를 세 하네스에 번들 MCP 서버로 실어 배포했다가 2026-09-02에 통째로 제거했습니다. PR #7644, 274개 파일 변경, rg -il codegraph 241건이 0건이 됐습니다. PR 본문이 OpenCode 플러그인에서 지운 것을 열거하며 이렇게 적었습니다.
the built-in
codegraphMCP, thecodegraph-bootstraphook, the config schema block, the tool-result init guidance, and the process-sweep family for its orphaned daemons.
고아 데몬을 수거하려고 만든 인프라를 도구와 함께 버렸습니다. 그 앞에는 좀비 청소 증거 디렉터리(2026-07-27)와 Windows 타임아웃 수리(2026-08-16)가 있습니다.
그런데 제거 사유는 어디에도 없습니다. PR 본문을 전문으로 읽었는데 무엇을 지웠는지, 설정 로더 변경이 왜 같이 실렸는지, QA 결과가 무엇인지만 있고 결정의 근거는 없습니다. 따라서 위 운영 이력은 정황이고 인과가 아닙니다. 이 구분을 지키지 않으면 증거를 넘겨 읽는 게 됩니다.
함께 실린 설정 로더 변경은 CodeGraph 탓이 아닙니다. OmoConfigLayerSchema가 .strict()여서 인식하지 못하는 키 하나에 설정 레이어 전체를 버렸고, codegraph 키를 지우면 그 블록을 갖고 있던 사용자의 설정이 조용히 초기화될 상황이었습니다. 번들로 싣는 쪽의 스키마 정책 문제입니다.
버전도 짚어둡니다. 이 사건들은 CodeGraph 1.5.0 기준이고 이번 측정은 1.6.0입니다. 1.6.0에서 무엇이 고쳐졌는지는 확인하지 못했고, 1 GiB 고아 폭풍은 재현하지 않았습니다.
ripwire — 감시 없이 신선한 유일한 그래프 도구
여기까지가 데몬 이야기입니다. redhat-et/ripwire v0.5.0이 정확히 이 지점을 겨냥합니다. C++23 단일 바이너리에 런타임 의존성이 없고, 전면에 No API key. No embeddings. No index server. No daemon.을 내겁니다. 그 마지막 문장이 앞 두 절의 축이므로 같은 세 저장소에 붙여 재봤습니다. 릴리스 타르볼을 받아 SHA-256을 대조했고, 설치 스크립트와 동봉된 에이전트 훅은 쓰지 않고 바이너리만 썼습니다.
데몬 없음 주장은 참입니다. 앞의 네 시나리오를 --no-cache와 --cache(증분) 두 모드 모두 통과했습니다. 특히 증분 모드가 삭제와 이동에서도 정확했습니다. CodeGraph(데몬 없음)와 Mantic이 존재하지 않는 경로를 답으로 주던 바로 그 두 칸입니다. ripgrep 말고 감시 프로세스 없이 그 표를 채운 유일한 도구입니다.
대가는 메모리와 지연입니다
README는 자기 저장소에서 0.25초 6.6 MB로 색인한다고 적었습니다. 실사용 저장소에서는 다른 수치가 나옵니다.
| 저장소 | 단계 | 벽시계 | 최대 메모리 | 캐시 |
|---|---|---|---|---|
| vllm 6,963 | 콜드 맵(캐시 없음) | 3.66 s | 628 MB | — |
| lean 캐시 생성 | 4.16 s | 649 MB | 58 MB | |
rich 캐시 채움(첫 --for) |
30.67 s | 3,471 MB | 280 MB | |
| 워엄 질의 | 1.81 s | 2,955 MB | 280 MB | |
| litellm 10,628 | rich 캐시 채움 | 25.10 s | 3,530 MB | 319 MB |
| 워엄 질의 | 2.60 s | 3,123 MB | 319 MB | |
| kubernetes 31,261 | rich 캐시 채움 | 8.03 s | 1,002 MB | 115 MB |
| 워엄 질의 | 2.49 s | 1,012 MB | 115 MB |
6,963 파일짜리 저장소에서 최대 3.5 GB를 씁니다. CodeGraph의 init 피크 1.06 GB의 3.3배입니다. 24 GB 머신에서 문제는 없었지만 여유 있는 값은 아닙니다. kubernetes가 vllm보다 파일이 4.5배 많은데 rich 단계는 더 쌉니다. Go 파일이 수는 많아도 개별 본문이 작아서로 보이는데 확인하지 않았으니 관측으로만 적습니다.
"No index server. No daemon."은 인덱스가 없다는 뜻이 아닙니다. 캐시 파일이 115~319 MB 있고 처음 채우는 데 8~31초가 듭니다. 없는 것은 상주 프로세스이지 인덱스가 아닙니다. 그 차이가 중요합니다. 상주 프로세스가 없으니 낡을 수 없고 좀비도 남지 않습니다.
정확도는 어떻게 묻느냐에 달렸습니다
같은 15과제를 두 어법으로 각각 물었습니다. 자연어(Mantic에 준 것과 같은 문구)와 심볼명(CodeGraph에 준 것과 같은 문구)입니다. ripwire 자신이 routed: name-exact BM25 — query names a symbol처럼 라우팅 결과를 밝히므로 두 경로가 실제로 다릅니다.
| 도구 | 정답 1위 | 상위 5위 | 발견 | 출력 토큰 중앙값 | 지연 중앙값 |
|---|---|---|---|---|---|
| CodeGraph | 15/15 | 15/15 | 15/15 | 484 | 248 ms |
| tgrep | 13/15 | 14/15 | 15/15 | 11 | 18 ms |
| ripgrep | 12/15 | 14/15 | 15/15 | 11 | 122 ms |
| ripwire (심볼명) | 10/15 | 13/15 | 13/15 | 264 | 2,372 ms |
| Mantic | 5/15 | 9/15 | 15/15 | 1,038 | 440 ms |
| ripwire (자연어) | 1/15 | 5/15 | 10/15 | 2,166 | 2,398 ms |
어법 하나로 1위가 1건에서 10건으로 바뀝니다. 자연어로 물으면 --for가 subtoken+body BM25로 빠지면서 넓게 퍼지고, 심볼명을 주면 name-exact 경로를 타고 좁혀집니다. 자연어를 받도록 설계된 Mantic과 대조적으로 ripwire의 --for는 이름을 알 때 가장 강합니다.
심볼명 모드에서도 CodeGraph에는 못 미칩니다. 출력은 264 토큰으로 CodeGraph의 484보다 싸지만 지연이 2,372 ms로 CodeGraph의 9.6배, tgrep의 132배입니다.
놓친 두 건은 성격이 다릅니다. litellm은 Python 본체 옆에 Rust 하위 프로젝트를 갖고 있는데, Router로 물으면 ripwire가 Rust 쪽 Router에 앵커를 걸고 .rs 파일 6개를 정답인 litellm/router.py 위에 올립니다. 커버리지 문제가 아닙니다. 별도 확인에서 같은 파일의 다른 심볼을 1위로 반환했으니 색인은 했고, 같은 이름이 두 언어에 있을 때 어느 쪽인지 고르지 못한 것입니다. 나머지 하나는 proxy_server가 심볼이 아니라 파일명이라 심볼 그래프 도구에 불리한 질문이었습니다. 과제 설계 문제로 보고 ripwire의 실패로 세지 않았습니다.
Markdown은 다른 verb로 들어옵니다
CodeGraph의 지배 변수가 언어 커버리지였고 Markdown을 보지 않아 md 과제가 0/5였습니다. ripwire는 Markdown을 지원 언어로 명시하는데, 확인해보니 사실이지만 --for로는 나오지 않습니다. kubernetes(YAML 6,391개, Markdown 591개)에서:
--for="contributing guidelines code review"→ 반환된 것은 전부.go. 문서 0건--recall="Contributor License Agreement"→ 문서 파일 323개 중 16개가 관련.CONTRIBUTING.md를 섹션 단위로(lines="7-12,13-20") 반환- 같은 질의를 CodeGraph에 →
No results found
--for는 코드 렌즈, --recall은 문서 렌즈로 분리돼 있습니다. 마크다운 헤딩이 섹션 심볼이 된다는 설명대로 동작합니다. CodeGraph에는 대응물이 아예 없으니 문서가 산출물인 저장소에서 ripwire가 메울 공백은 실재합니다. 다만 에이전트가 어느 verb를 부를지 알아야 합니다.
판정
staleness가 가장 걱정이라면 ripwire가 현재 유일한 답입니다. 심볼·호출 그래프 수준의 답을 내면서 감시 프로세스 없이 항상 신선한 도구는 이것뿐입니다. CodeGraph의 데몬 생존 문제, Mantic의 유령 경로, tgrep CLI의 거짓 음성이 여기에는 없습니다.
그 외의 축에서는 기존 도구가 낫습니다.
| 축 | 최고 | ripwire |
|---|---|---|
| 정답 1위 | CodeGraph 15/15 | 10/15 (심볼명) |
| 질의 지연 | tgrep 18 ms | 2,372 ms |
| 최대 메모리 | tgrep 121~159 MB | 1.0~3.5 GB |
| 출력 토큰 | tgrep·rg 11 | 264 |
v0.5.0이고 측정 시점 기준 릴리스가 하루 전입니다. 1.0 이전이며 이번 측정은 저장소 3개, 15과제입니다.
토큰은 도구보다 질의 폭이 지배합니다
"이모지 차단 로직 이해하기" 한 과제에서 잰 것입니다.
| 방법 | 바이트 |
|---|---|
Mantic --files |
253 |
CodeGraph context --no-code |
1,134 |
| Mantic 기본 JSON | 2,712 |
CodeGraph context |
4,064 |
| 대상 파일 통째로 읽기 | 5,920 |
ripgrep -l 후보 목록 (154개) |
10,015 |
CodeGraph explore |
20,609 |
두 가지가 뒤집힙니다. 순위 없는 -l 목록이 정답 파일을 통째로 읽는 것보다 비쌉니다. 최악의 과제에서는 ripgrep이 1,709개 파일 목록에 152,439바이트를 썼는데 정답 파일 하나는 5,920바이트입니다. 그리고 CodeGraph explore는 파일 읽기의 3.5배입니다. "fewer tokens" 주장은 어느 하위 명령을 쓰느냐에 달렸습니다.
여기서 나오는 개선은 도구를 바꾸지 않고도 됩니다. 넓은 rg -l을 던지고 대량 목록을 받아 읽는 패턴을 버리는 것입니다. 패턴을 좁히거나 -m으로 제한하는 편이 낫습니다.
발표된 주장의 검증
| 주장 | 판정 |
|---|---|
| tgrep "최대 52배" | 재현 안 됨. 배수는 쿼리 선택도의 함수이고 0.25x ~ 206x로 움직인다 |
| Mantic "ripgrep보다 2-10배 느림" | 부분적으로 틀림. 소형에서 5배 느리지만 linux에서는 4배 빠르다 |
| CodeGraph "fewer tokens" | 하위 명령에 따라 뒤집힌다. context --no-code 1,134 B, explore 20,609 B |
| CodeGraph "auto syncs on code changes" | 조건부 참. 감시자는 MCP 서버 안에 있고 CLI 단독에는 없다 |
| Mantic "100% multi-repo accuracy" | 중첩 저장소에서 반증됨. 1,007개 파일을 하나도 반환하지 않았다 |
| ripwire "No index server. No daemon." | 참이되 인덱스가 없다는 뜻은 아니다. 캐시 115~319 MB, 첫 채움 8~31초 |
그래서 무엇을 쓰나
기본은 ripgrep입니다. 40개 과제에서 정답을 놓치지 않은 도구는 ripgrep과 tgrep뿐이고, 편집 직후에도 항상 정확한 것은 ripgrep뿐입니다. 인덱스도 데몬도 없으니 낡거나 죽을 상태 자체가 없습니다.
도구마다 물어야 할 질문이 다릅니다.
| 도구 | 지배 변수 | 검토할 때 물을 것 |
|---|---|---|
| tgrep | 쿼리 종류 | 내가 던지는 쿼리가 주로 어떤 종류인가 |
| CodeGraph | 언어 커버리지 | 내 저장소 주력 언어가 지원 목록에 있나 |
| Mantic | 경로의 서술성 | 파일 이름만 보고 답을 맞힐 수 있는 저장소인가 |
| ripwire | 질문의 어법 | 심볼 이름을 알고 묻는가, 자연어로 묻는가 |
tgrep은 네 조건이 겹칠 때 채택합니다. 저장소가 크고, 세션이 길어 쿼리 수가 손익분기를 넘고, 쿼리가 희귀 리터럴이나 -l 위주이고, tgrep serve가 상시 떠 있을 때입니다. 그 조합에서 206배가 나왔고 GitHub Copilot CLI가 tgrep을 붙인 것도 이 조건입니다. 이번 여덟 저장소 중에는 넷을 다 만족하는 곳이 없었습니다. 이미 쓰고 있다면 유지할 이유는 있습니다. 낡아도 없는 경로를 지어내지 않으니까요.
CodeGraph는 codegraph status의 "Files by Language"를 먼저 봅니다. 주력 확장자가 거기 없으면 다른 장점은 검토할 필요가 없습니다. 있으면 강력합니다. impact / callers / callees / affected는 grep 계열에 대응물이 없습니다. 대신 MCP로 붙여 데몬을 띄우고, CLI나 훅에서 부른다면 매번 codegraph index를 먼저 돌립니다. 배포 제품에 번들로 싣는 건 권하지 않습니다.
ripwire는 데몬을 띄울 수 없는 자리를 메웁니다. CI, 훅, 일회성 스크립트처럼 상주 프로세스를 둘 수 없거나 데몬 관리 비용을 감당하지 않기로 한 경우입니다. 데몬을 띄우고 관리할 수 있는 환경이라면 CodeGraph가 더 정확하고 9배 빠릅니다. 쓴다면 질의는 심볼명으로 던지고 문서를 찾을 때는 --recall을 씁니다. 메모리 3.5 GB를 감당할 수 있는지 먼저 봅니다.
Mantic은 권하지 않습니다. 여기에 역설이 있습니다. 파일 이름만 보고 답을 맞힐 수 있는 저장소라면 사람도 맞힐 수 있어 도구가 덜 필요하고, 아니라면 도구가 틀립니다. --files 출력이 253바이트로 가장 싸고 맞힐 때는 거의 1위라는 건 기억할 만하지만, 중첩 저장소 실명과 확신에 찬 오답을 넘지 못합니다.
확인하지 못한 것
- 콜드 캐시.
purge에 sudo가 필요해 양쪽 다 웜 캐시 측정입니다. 실사용 첫 쿼리는 더 느립니다 - MCP 세션 단위 측정. 두 도구의 핵심 주장인 "툴 호출 횟수 감소"를 CLI 호출로만 대신 쟀습니다
- 에이전트의 실제 질의 분포. 토큰 회계 결론이 여기 달려 있는데, 좁은 패턴과 넓은 키워드 중 어느 쪽이 실사용에 가까운지 확정하지 못했습니다
- 리눅스에서 CodeGraph 색인이 실패한 원인. 1회 관측이고 외부 증거가 사전 확률을 높이지만 인과를 증명하지는 않습니다
- 1.5.0에서 1.6.0으로 오면서 고아 데몬 문제가 무엇이 고쳐졌는지. 재현 시도를 설계해 돌린 것은 아닙니다
- ripwire의 1.0 이후 거동. v0.5.0을 저장소 3개, 15과제로만 쟀습니다
- 관련도 랭킹이 없는 도구의 1위 횟수. 같은 15과제를 두 번 돌렸더니 ripgrep의 1위가 12건과 14건으로 갈렸습니다(
lite-02,k8s-04). tgrep은 두 번 다 13건이었습니다. grep 계열의 순위는 탐색 순서라 이 숫자를 정밀하게 읽으면 안 됩니다