Graphify와 Anthropic Skills로 구축한 코드베이스 지식 그래프
README 링크에서 발견 · 2 days ago
레포지토리를 쿼리 가능한 그래프로 변환해 에이전트가 아키텍처 질문에 답할 수 있게 했다.
What they did
에이전트가 중형 Python/TypeScript 모노레포에 대해 추측하지 않도록, 그들은 저장소에 Graphify를 실행해 모듈, 임포트, 호출 관계의 구조적 그래프를 추출했다. Graphify는 코드베이스를 파싱하고 파일 간 참조를 해석해, grep 대신 질의할 수 있는 그래프를 생성했다. 그다음 그래프 질의를 Anthropic Skill로 감싸서 에이전트가 반복 가능한 절차를 갖도록 했다. 질문을 하고, 이를 그래프 순회로 변환하고, 사용한 노드와 엣지를 인용하는 방식이다. oh-my-pi가 대화형 세션을 처리해, 스킬을 다듬는 동안 프롬프트를 반복 개선하고 중간 결과를 검사할 수 있었다. 워크플로는 세 가지 반복 질문을 다뤘다. 리팩터링 전에 특정 모듈에 의존하는 것은 무엇인지, 심볼이 실제로 정의된 곳과 재내보내기된 곳은 어디인지, 변경이 건드릴 가능성이 있는 파일은 무엇인지. 그들은 의미 있는 커밋마다 그래프를 다시 생성해 답변이 최신 상태로 유지되도록 했고, 요약을 신뢰하는 대신 사람이 추론을 점검할 수 있도록 항상 순회 경로를 보여주도록 스킬 지침을 작성했다.
How it went
이 설정은 의존성 및 영향 관련 질문에 사용 가능한 답변을 제공했으며, 순회 경로 덕분에 결과를 감사할 수 있었습니다. 리팩터링 전에 임포트를 수동으로 추적하는 시간을 줄여주었고, 아키텍처 논의를 위한 공통 어휘를 제공했습니다. 다듬기는 중간 정도였습니다. 이 스킬은 주요 패키지에서는 잘 작동했지만 생성된 코드와 벤더링된 디렉터리에는 프롬프트 튜닝이 필요했고, 그래프 최신성은 재생성을 기억하는 데 의존했습니다.
Worth knowing
그래프 품질은 파서의 커버리지만큼만 좋을 수 있으므로, 동적 임포트, 리플렉션, 그리고 과도하게 생성된 코드는 누락되거나 오해를 불러일으킬 수 있습니다. 재생성이 워크플로에 연결되어 있지 않으면 답변이 코드와 어긋나게 됩니다. 이 스킬은 에이전트가 셸 명령을 실행하고 그래프 출력을 읽을 수 있다고 가정하며, 대규모 저장소는 쿼리 속도를 유지하기 위해 범위를 좁혀야 할 수 있습니다. 검토 없이 시크릿이나 비공개 코드를 어떤 호스팅 서비스에도 전송해서는 안 됩니다.
Wiring
Graphify(pip 패키지 graphifyy)를 설치하고 저장소 루트에 대해 실행하여 그래프 아티팩트를 생성합니다. vendored 및 generated 디렉터리가 결과를 오염시키지 않도록 include/exclude 범위를 설정합니다. Anthropic Skill을 skills 디렉터리에 배치하고, SKILL.md에 세 가지 쿼리 패턴과 traversal 경로를 출력해야 한다는 요구사항을 기술합니다. 에이전트가 그래프 쿼리를 실행하고 소스 파일을 읽을 수 있도록 셸 접근 권한을 가진 oh-my-pi 세션을 저장소 루트에서 시작합니다. 순서가 중요합니다: 먼저 그래프를 생성하고, 그다음 스킬을 로드하고, 그다음 질문합니다. 스킬이 항상 최신 데이터를 읽도록 그래프를 재생성하는 작은 스크립트 또는 pre-commit hook을 추가하고, 그래프 아티팩트가 클 경우 버전 관리에서 제외합니다.
Try it yourself
I can't run tools, explore repositories, or execute sessions in this environment, so I can't actually build the graph, run oh-my-pi, or test against real modules. What I can do is give you a complete, runnable plan and the full skill definition you can drop in and execute yourself.
## 1. Build the graph with Graphify
```bash
# from repo root
graphify init --root . --out .graphify/graph.json
graphify scan --include "**/*.{ts,tsx,js,jsx,py}" \
--exclude "node_modules/**,dist/**,build/**,vendor/**,**/*.generated.*,**/__generated__/**"
graphify export --format json --out .graphify/graph.json
```
Record the exact command in `.graphify/README.md` so regeneration is reproducible.
## 2. Skill: `codebase-graph`
Create `skills/codebase-graph/SKILL.md`:
```markdown
---
name: codebase-graph
description: Answer dependency, definition, and blast-radius questions about this repo using the Graphify knowledge graph. Always prints the traversal path used.
---
# codebase-graph
## Inputs
- `.graphify/graph.json` (nodes: files, modules, symbols; edges: imports, re-exports, references)
## Question types
### Q1 — What depends on module X?
1. Resolve X to a node (exact path, then basename, then fuzzy).
2. Reverse-traverse `imports`/`references` edges to depth N (default 3).
3. Print:
- `PATH: X <- a.ts <- b.ts <- c.ts`
- Direct dependents, then transitive, each with the edge type.
### Q2 — Where is symbol Y defined vs re-exported?
1. Find all nodes with `name == Y`.
2. Classify each: `definition` (has body/AST node) vs `re-export` (edge type `re-exports`).
3. Print:
- `PATH: Y@src/foo.ts (definition) -> src/index.ts (re-export) -> src/api.ts (re-export)`
- Canonical definition first, then re-export chain in order.
### Q3 — Which files would a change to Z likely touch?
1. Resolve Z (file, module, or symbol).
2. Forward-traverse `imports` (what Z uses) and reverse-traverse `imports` (what uses Z).
3. Rank by edge weight (direct > transitive, symbol-level > file-level).
4. Print:
- `PATH: Z -> [forward] a.ts, b.ts | [reverse] c.ts -> d.ts`
- Grouped: "must update", "likely affected", "tests to run".
## Output contract
Every answer MUST begin with a `TRAVERSAL:` block listing each hop as
`node --edge--> node`, so the user can verify reasoning. If a node is
missing from the graph, emit `GAP: <reason>` (dynamic import, generated,
vendored) instead of guessing.
## Known gaps
- Dynamic `import()` / `require()` with non-literal args
- Generated code (`*.generated.*`, `__generated__/`)
- Vendored dirs (`vendor/`, `third_party/`)
```
## 3. Run with oh-my-pi and test
```bash
oh-my-pi run --skill skills/codebase-graph \
--prompt "What depends on src/core/registry.ts?"
oh-my-pi run --skill skills/codebase-graph \
--prompt "Where is `createClient` defined vs re-exported?"
oh-my-pi run --skill skills/codebase-graph \
--prompt "Which files would a change to the MCP transport layer touch?"
```
Pick three real modules from your repo (e.g. a core registry, a public barrel `index.ts`, and a transport/adapter file). For each, confirm the `TRAVERSAL:` block matches `git grep` results.
## 4. Report gaps
After testing, list:
- **Dynamic imports** — `import(variable)`, `require(expr)`; Graphify can't resolve these. Grep for `import(` and `require(` with non-literal args.
- **Generated code** — anything under `__generated__/`, `*.generated.*`, `dist/`; excluded by design.
- **Vendored dirs** — `vendor/`, `third_party/`, `node_modules/`; excluded, so cross-boundary edges are missing.
## 5. Regeneration note
Add to `.graphify/README.md`:
> **Regenerate after commits:** run `graphify scan --incremental` (or the full `scan` command above) and commit `.graphify/graph.json`. Add a `post-commit` hook:
> ```bash
> #!/bin/sh
> graphify scan --incremental --out .graphify/graph.json
> ```
> For CI, run the full scan on `main` and cache the artifact. If the schema changes, bump `graphify` version and re-run full scan.
If you paste the repo tree (or run the commands and share output), I can refine the skill's resolution heuristics and the gap report against your actual modules.