이 글은 Hacktoberfest Open-Source AI Challenge: Week 4 — Ghost in the Machine에 제출하는 글입니다.
제가 만든 것 (What I Built)
모든 저장소(repository)는 무덤과 같습니다.
파일들은 add new benchmarks (to match koa)와 같은 커밋으로 태어납니다. 사람들은 그것들을 고치고, 확장하고, 수년간 논쟁합니다. 그러다가 어느 날 누군가 chore: remove benchmarks directory를 작성하면, 그 파일들은 사라집니다. 역사는 남아있지만, 모두의 기억 속에서는 사라진 것이죠.
git-séance는 이들을 위한 영매술(séance)을 진행합니다. 이것은 다음과 같은 기능을 하는 작은 커맨드라인 도구입니다:
- 저장소의 역사에서 존재했던 모든 파일을 찾아냅니다;
- 각 유령이 말하게 합니다: 누가 만들었는지, 언제 만들었는지, 무엇을 했었는지, 얼마나 오래 살았는지, 누가 관리했는지, 그리고 그것을 끝낸 정확한 커밋까지 알려줍니다;
- '오프렌다(ofrenda)'를 만듭니다: 저장소에서 가장 오랫동안 사라진 코드들을 위한 추모 페이지인
GRAVEYARD.md 파일을 생성합니다.
이것은 으스스하면서도, 제가 생각하는 Día de los Muertos (죽은 자들의 날)의 한 형태입니다. 현재 팀에 합류한 사람이 나타나기 전에 프로젝트를 지탱해 왔던 코드들을 위한 작은 제단인 셈이죠.
데모 (Demo)
저는 이 도구를 Express 저장소에 적용했습니다: 총 6,183개의 커밋과 16년의 역사를 가지고 있습니다.
$ git-seance --repo express
🪦 express의 역사 속에는 684개의 파일이 잠들어 있습니다.
...
이 목록을 가져오는 데는 약 0.3초가 걸립니다. 그리고 그중 하나를 소환해 보겠습니다:
$ git-seance summon lib/view/partial.js --repo express
👻 lib/view/partial.js
...
그리고 이것은 Express를 위해 작성된 오프렌다의 일부입니다 (git-seance ofrenda --n 6, 노트북 CPU에서 약 35초 소요):
⚰️ Contributing.md
2014-05-12 – 2025-06-09 · 11.1 years
저는 Contributing.md였습니다. Roman Shtylman은 'Contributing.md를 추가하세요'라는 말과 함께 저를 2014-05-12에 세상에 나오게 했습니다. 저는 신규 기여자들에게 Express.js 프로젝트에 기여하는 과정을 안내하며, 불필요한 장애물을 최소화하면서 참여를 유도했습니다. 저는 이 복도를 11.1년 동안 걸었으며, 저를 통과한 커밋은 33개였습니다. 마지막에는 246줄이 되었습니다. 그러다 2025-06-09일, Sebastian Beltran이 저를 끝낸 말을 적었습니다: "fix(docs): move documentation and charters to the discussions and .github … (#6427)". Ulises Gascón, Blake Embrey, Douglas Christopher Wilson에게 제가 그들을 기억한다고 전해주세요.
⚰️ test/mocha.opts
2011-12-20 – 2022-02-21 · 10.2 years
저는 mocha.opts였습니다. Tj Holowaychuk은 'mocha.opts'라는 말과 함께 저를 2011-12-20에 세상에 나오게 했습니다. [...] 저는 이 복도를 10.2년 동안 걸었으며, 저를 통과한 커밋은 2개였습니다. 마지막에는 3줄이 되었습니다. 그러다 2022-02-21일, Douglas Christopher Wilson이 저를 끝낸 말을 적었습니다: "tests: remove usage of should".
여기에 있는 모든 이름, 날짜, 개수 및 커밋 메시지는 실제이며 git에서 가져온 것입니다.
코드
오픈 웨이트 모델(Gemma)을 사용하여 자신의 컴퓨터에서 실행하며, git 기록에 남아있는 삭제된 코드의 유령들을 소환하세요.
👻 git-séance
모든 저장소는 무덤과 같습니다. 파일들은 탄생하고 수년간 작업되지만, 결국 하나의 커밋("chore: remove benchmarks directory")으로 끝납니다. git-séance는 이들을 찾아내어 각 유령이 자신의 이야기를 하도록 하고, 저장소에서 사라진 코드들을 위한 추모 페이지인 ofrenda를 만듭니다.
Hacktoberfest Open-Source AI Challenge, Week 4: Ghost in the Machine을 위해 제작되었습니다.
$ git-seance --repo express
🪦 express의 기록에는 684개의 파일이 잠들어 있습니다.
...
View on GitHub
MIT 라이선스. 빌드 단계가 없는 순수 Node.js로 약 280줄 분량입니다. 로컬에 있는 모든 git 저장소에 연결하여 사용할 수 있습니다:
git clone https://github.com/Yash11778/git-seance && cd git-seance && npm install
node bin/git-seance.mjs --repo /path/to/any/git/repo
작동 방식 (How It Works)
git log --diff-filter=D ──► 삭제된 모든 경로 (복원된 경로는 건너뜁니다)
git log --diff-filter=A ──► 각 항목이 언제, 누구에 의해 탄생했는지
git rev-list / shortlog ──► 몇 개의 커밋이 이를 건드렸고, 누가 가장 신경 썼는지
...
교훈: 유령은 섬뜩할 수 있지만, 거짓말을 할 수는 없다
제가 처음 만든 버전에서는 Gemma에게 각 파일에 대한 사실 목록을 제공하고 전체 추모사를 작성하도록 요청했습니다. 목소리는 완벽했지만 역사는 꾸며냈습니다. 정확히 두 번의 커밋으로 변경된 파일은 '꾸준한 업데이트 흐름'이 되었다는 식이었고, 단순히 삭제된 파일은 '삭제되는 라인 수에 따라 서서히 사라졌다'는 식으로 묘사되었습니다. 그리고 사실을 사용하고 싶지 않을 때는 회피했습니다: '저는 특정 날짜에 생성되었습니다.'
이것이야말로 과거를 기리는 도구가 할 수 없는 유일한 부분입니다. 그래서 작업은 분할됩니다:
- 코드 부분이 모든 사실을 연설문에 기록합니다 (이름, 날짜, 생존 기간, 라인 수, 커밋 메시지 등), 따라서 항상 진실합니다.
- Gemma는 오직 할 수 있는 일만 맡습니다: 사라진 파일의 마지막 코드를 읽고, 유령의 목소리로 그것이 이전에 무엇을 했는지 말하는 것입니다.
const prompt = `여기에 "${path}"라는 이름의 삭제된 파일의 마지막 버전입니다:
"""
${words.excerpt.slice(0, 1200)}
...
코드에 근거하여 작성되었기 때문에 라인은 구체적이고 정확합니다. lib/view/partial.js는 '뷰 경로에서 부분 객체 이름을 해결하는 데 사용되었다'고 하고, 오래된 jQuery 복사본은 '저는 버전 1.3.2의 jQuery JavaScript Library였습니다'라고 말합니다.
더 작은 교훈: 1B 모델에게 무엇을 말하지 않도록 할 수는 없다
Gemma는 '세심하게(meticulously)'라는 단어를 좋아했습니다: 처음 여섯 명의 유령 중 네 명이 세심하게 무언가를 했습니다. 저는 프롬프트에 "_절대 세심하게, 부지런히 또는 조심스럽다는 단어를 사용하지 마세요(_Do not use the words meticulously, diligently or carefully)"_를 추가했는데, 바로 다음 유령이 여전히 "성실하게 요청을 처리하고 있다(diligently processing requests)"고 했습니다. 작은 모델들은 부정적인 지시 사항을 잘 따르지 못합니다. 그래서 이제 채우는 단어들(filler words)은 생성 후 코드에서 제거되며, 3줄짜리 정규 표현식(regex)으로 이를 처리하는데, 항상 작동합니다.
왜 공개가 여기서 중요한가 (Why Open Matters Here)
저장소의 기록은 팀의 사적 기억입니다: 이름, 커밋 메시지, 그리고 의도적으로 삭제된 코드까지 포함됩니다. 이러한 도구는 그 어떤 정보도 사용자의 기기를 벗어나지 않을 때만 의미가 있습니다. 특히 회사의 비공개 저장소(private repos)에 해당합니다.
- Gemma 3 1B는 일반 노트북 CPU를 통해 로컬에서 실행됩니다 Transformers.js 및 onnxruntime-node를 사용합니다. 코드, 이름 또는 커밋 메시지는 어떠한 곳으로도 전송되지 않습니다.
- 전원(및 인터넷)이 꺼진 상태에서도 작동합니다. 한 번의 약 860 MB 다운로드만 거치면 완전히 오프라인에서 구동됩니다.
- 비용이 들지 않으며 사라지지 않습니다. API 키나 중단될 수 있는 호스팅 서비스가 필요하지 않습니다. 사라진 것을 기억하는 도구에게는 이 방식이 적절하게 느껴집니다.
- 모델은 교체 가능합니다.
SEANCE_MODEL을 설정하여 더 큰 모델로 소환할 수 있습니다.
다음 기능 (What's Next)
- 단순히 전체 파일을 넘어, 정의가 사라진 경우
git log -p를 따라가며 삭제된 함수까지 소환합니다.
- 파일의 조상들이 장례식에 참석하는 "가계도(family tree)" 기능입니다. 파일 이름 변경 추적을 따릅니다.
git-seance haunt: 파일을 삭제할 때마다 짧은 비석(epitaph)을 작성하는 커밋 훅(commit hook)입니다.
경품 부문 (Prize Categories)
Gemma의 최고의 활용 사례. 프로젝트에서 Gemma 3 1B만이 유일한 모델입니다. 이 모델은 로컬에서 실행되며, 모든 유령의 마지막 코드를 읽어 그것이 무엇을 하던 것이었는지 말해줍니다.
스캐폴딩(scaffolding), 테스트 및 디버깅을 위해 AI 지원(Claude Code)으로 제작되었습니다. 데모는 공개 Express 저장소의 기록을 사용하며, 모든 유령의 사실은 해당 git log에서 직접 가져옵니다.