Back to productivity
productivity v1.0.0 6.4 min read 384 lines

snippet-manager

코드 스니펫, 명령어, 템플릿을 저장·검색·관리 — 자주 쓰는 조각을 태그와 카테고리로 체계적으로 관리

Hermes Agent
MIT

스니펫 관리자

자주 쓰는 코드 조각, 명령어, 템플릿을 저장하고 빠르게 불러오는 스킬. JSON 파일 기반의 가벼운 스니펫 저장소입니다.

언제 사용할까

  • "이 명령어 기억해줘" / "나중에 쓸 수 있게 저장해줘"
  • "이전에 저장한 스니펫 찾아줘"
  • "자주 쓰는 git 명령어 모아둔 거 있어?"
  • 프로젝트별 코드 템플릿 관리

저장소 구조

~/.hermes/data/snippets/
├── snippets.json # 메인 스니펫 DB
└── collections/ # 컬렉션별 파일 (선택)
├── python.json
├── git.json
└── docker.json

snippets.json 형식

[
{
"id": "uuid",
"name": "이름",
"description": "설명",
"content": "실제 코드/명령어 내용",
"language": "python|bash|sql|yaml|json|text|...",
"tags": ["tag1", "tag2"],
"collection": "python",
"created": "2024-01-15T10:00:00",
"updated": "2024-01-15T10:00:00",
"usage_count": 5
}
]

1. 스크립트 설치

스니펫 관리 스크립트를 ~/.hermes/scripts/snippet_manager.py에 생성:

#!/usr/bin/env python3
"""스니펫 관리자 CLI"""
import json, os, sys, uuid, re
from datetime import datetime
from pathlib import Path

SNIPPET_DIR = Path.home() / ".hermes" / "data" / "snippets"
SNIPPET_FILE = SNIPPET_DIR / "snippets.json"

def load_snippets():
SNIPPET_DIR.mkdir(parents=True, exist_ok=True)
if SNIPPET_FILE.exists():
with open(SNIPPET_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
return []

def save_snippets(snippets):
SNIPPET_DIR.mkdir(parents=True, exist_ok=True)
with open(SNIPPET_FILE, 'w', encoding='utf-8') as f:
json.dump(snippets, f, ensure_ascii=False, indent=2)

def add(name, content, description="", language="text", tags=None, collection="general"):
snippets = load_snippets()
snippet = {
"id": str(uuid.uuid4())[:8],
"name": name,
"description": description,
"content": content,
"language": language,
"tags": tags or [],
"collection": collection,
"created": datetime.now().isoformat(),
"updated": datetime.now().isoformat(),
"usage_count": 0
}
snippets.append(snippet)
save_snippets(snippets)
print(f"✅ 스니펫 저장: {name} (id: {snippet['id']})")
return snippet

def search(query, tag=None, collection=None, language=None):
snippets = load_snippets()
results = []
q = query.lower() if query else ""

for s in snippets:
match = True
if q:
match = (q in s['name'].lower() or
q in s['description'].lower() or
q in s['content'].lower() or
any(q in t.lower() for t in s['tags']))
if tag and tag.lower() not in [t.lower() for t in s['tags']]:
match = False
if collection and s.get('collection', 'general') != collection:
match = False
if language and s.get('language', 'text') != language:
match = False
if match:
results.append(s)

# 사용 빈도순 정렬
results.sort(key=lambda x: x.get('usage_count', 0), reverse=True)
return results

def get(snippet_id):
snippets = load_snippets()
for s in snippets:
if s['id'] == snippet_id or s['name'].lower() == snippet_id.lower():
# 사용 횟수 증가
s['usage_count'] = s.get('usage_count', 0) + 1
save_snippets(snippets)
return s
return None

def delete(snippet_id):
snippets = load_snippets()
original_len = len(snippets)
snippets = [s for s in snippets if s['id'] != snippet_id and s['name'].lower() != snippet_id.lower()]
if len(snippets) < original_len:
save_snippets(snippets)
print(f"🗑️ 스니펫 삭제됨")
return True
print(f"❌ 스니펫을 찾을 수 없음: {snippet_id}")
return False

def list_collections():
snippets = load_snippets()
collections = {}
for s in snippets:
col = s.get('collection', 'general')
collections[col] = collections.get(col, 0) + 1
return collections

def list_tags():
snippets = load_snippets()
tags = {}
for s in snippets:
for t in s.get('tags', []):
tags[t] = tags.get(t, 0) + 1
return dict(sorted(tags.items(), key=lambda x: x[1], reverse=True))

CLI 인터페이스


if __name__ == "__main__":
if len(sys.argv) < 2:
print("사용법: snippet_manager.py [args]")
print(" add [--desc ...] [--lang ...] [--tags tag1,tag2] [--col collection]")
print(" search [--tag tag] [--col collection] [--lang language]")
print(" get ")
print(" delete ")
print(" list [--col collection] [--tag tag]")
print(" collections")
print(" tags")
print(" stats")
sys.exit(1)

cmd = sys.argv[1]

if cmd == "add":
# 간단 추가: name, content 순서
name = sys.argv[2] if len(sys.argv) > 2 else input("이름: ")
content = sys.argv[3] if len(sys.argv) > 3 else sys.stdin.read().strip()

desc, lang, tags, col = "", "text", [], "general"
i = 4
while i < len(sys.argv):
if sys.argv[i] == '--desc' and i+1 < len(sys.argv):
desc = sys.argv[i+1]; i += 2
elif sys.argv[i] == '--lang' and i+1 < len(sys.argv):
lang = sys.argv[i+1]; i += 2
elif sys.argv[i] == '--tags' and i+1 < len(sys.argv):
tags = sys.argv[i+1].split(','); i += 2
elif sys.argv[i] == '--col' and i+1 < len(sys.argv):
col = sys.argv[i+1]; i += 2
else:
i += 1

add(name, content, desc, lang, tags, col)

elif cmd == "search":
query = sys.argv[2] if len(sys.argv) > 2 else ""
tag, col, lang = None, None, None
i = 3
while i < len(sys.argv):
if sys.argv[i] == '--tag': tag = sys.argv[i+1]; i += 2
elif sys.argv[i] == '--col': col = sys.argv[i+1]; i += 2
elif sys.argv[i] == '--lang': lang = sys.argv[i+1]; i += 2
else: i += 1

results = search(query, tag, col, lang)
for r in results[:20]:
print(f"📌 [{r['id']}] {r['name']}")
print(f" {r['description']}")
print(f" 언어: {r.get('language', 'text')} | 컬렉션: {r.get('collection', 'general')} | 태그: {', '.join(r.get('tags', []))}")
print(f" 사용: {r.get('usage_count', 0)}회 | 생성: {r['created'][:10]}")
print()

elif cmd == "get":
result = get(sys.argv[2])
if result:
print(f"📌 {result['name']}")
print(f" {result['description']}")
print("---")
print(result['content'])
else:
print("❌ 찾을 수 없음")

elif cmd == "delete":
delete(sys.argv[2])

elif cmd == "list":
col, tag = None, None
i = 2
while i < len(sys.argv):
if sys.argv[i] == '--col': col = sys.argv[i+1]; i += 2
elif sys.argv[i] == '--tag': tag = sys.argv[i+1]; i += 2
else: i += 1

results = search("", tag=tag, collection=col)
print(f"📋 총 {len(results)}개 스니펫")
for r in results:
print(f" [{r['id']}] {r['name']} ({r.get('language', 'text')}) — {', '.join(r.get('tags', []))}")

elif cmd == "collections":
cols = list_collections()
for k, v in cols.items():
print(f" 📁 {k}: {v}개")

elif cmd == "tags":
tags = list_tags()
for k, v in list(tags.items())[:20]:
print(f" 🏷️ {k}: {v}개")

elif cmd == "stats":
snippets = load_snippets()
print(f"📊 스니펫 통계")
print(f" 총 스니펫: {len(snippets)}개")
print(f" 컬렉션: {len(list_collections())}개")
print(f" 태그: {len(list_tags())}개")
if snippets:
top = max(snippets, key=lambda x: x.get('usage_count', 0))
print(f" 가장 많이 쓴 스니펫: {top['name']} ({top.get('usage_count', 0)}회)")

2. ICBM2 사용 가이드

스니펫 관리는 execute_code로 스크립트를 실행하거나, 직접 JSON을 조작합니다.

스니펫 저장

import json, os
from datetime import datetime

SNIPPET_FILE = os.path.expanduser("~/.hermes/data/snippets/snippets.json")
os.makedirs(os.path.dirname(SNIPPET_FILE), exist_ok=True)

기존 스니펫 로드


if os.path.exists(SNIPPET_FILE):
with open(SNIPPET_FILE) as f:
snippets = json.load(f)
else:
snippets = []

새 스니펫 추가


snippet = {
"id": "abc12345",
"name": "Docker 컨테이너 로그 확인",
"description": "실행 중인 컨테이너의 실시간 로그를 확인하는 명령어",
"content": "docker logs -f --tail 100 ",
"language": "bash",
"tags": ["docker", "logs", "debugging"],
"collection": "docker",
"created": datetime.now().isoformat(),
"updated": datetime.now().isoformat(),
"usage_count": 0
}
snippets.append(snippet)

with open(SNIPPET_FILE, 'w') as f:
json.dump(snippets, f, ensure_ascii=False, indent=2)

스니펫 검색

import json, os

SNIPPET_FILE = os.path.expanduser("~/.hermes/data/snippets/snippets.json")
with open(SNIPPET_FILE) as f:
snippets = json.load(f)

키워드 검색


query = "docker"
results = [s for s in snippets if query in s['name'].lower()
or query in s['description'].lower()
or query in s['content'].lower()
or any(query in t for t in s['tags'])]

for r in results:
print(f"📌 {r['name']} [{r.get('language', 'text')}]")
print(f" {r['description']}")
print(f" {r['content'][:100]}...")
print()

3. 유용한 기본 스니펫 세트

초기 설정 시 다음 스니펫을 사전 등록하면 유용:

Git 스니펫


이름: git force pull
내용: git fetch origin && git reset --hard origin/main
태그: git, force, sync

Python 스니펫


이름: pandas quick EDA
내용: df.info(); df.describe(); df.isnull().sum()
태그: python, pandas, eda

Docker 스니펫


이름: docker cleanup
내용: docker system prune -af --volumes
태그: docker, cleanup

네트워크 스니펫


이름: 포트 사용 확인
내용: lsof -i :PORT_NUMBER
태그: network, port, debugging

4. 추천 컬렉션 구조

| 컬렉션 | 용도 | 예시 |
|--------|------|------|
| git | Git 명령어 | rebase, cherry-pick, stash |
| docker | Docker/K8s | build, compose, logs |
| python | Python 코드 | pandas, requests, asyncio |
| bash | 셸 명령어 | find, sed, awk |
| sql | SQL 쿼리 | JOIN, window function, CTE |
| network | 네트워크 | curl, ssh, port forwarding |
| macos | macOS 특정 | defaults, brew, pmset |
| templates | 코드 템플릿 | FastAPI boilerplate, React component |

5. Notion 연동 (선택)

스니펫을 Notion DB에도 저장하려면 notion 스킬을 함께 사용:

# Notion 스니펫 DB에 추가 (기존 notion 스킬의 API 사용)

DB ID: 필요시 생성


필드: 이름(Title), 설명(Text), 내용(Rich Text), 언어(Select), 태그(Multi-select)


주의사항

  • 민감한 정보(비밀번호, API 키, 토큰)는 절대 스니펫에 저장하지 말 것
  • ~/.hermes/secrets/에 보안 정보를, 스니펫에는 참조 방법만 기록
  • 스니펫 ID는 8자리 UUID 축약형 — 충돌 확률 극히 낮음

Related Skills / 관련 스킬

google-workspace

Gmail, 캘린더, 드라이브, 연락처, 시트, 문서 통합 — gws CLI + OAuth2

linear

Linear 이슈/프로젝트/팀 관리 — GraphQL API, API 키 인증, curl만으로 동작

maps

>

nano-pdf

자연어 명령으로 PDF 편집 — 텍스트 수정, 오타 교정, 제목 업데이트