프롬프트를 자동 수집하고, 교정 패턴을 분석해서, AI와의 소통을 개선하는 시스템.
Claude Code 훅 2개 + 커맨드 1개로 완성됩니다.
/프롬프트분석 커맨드로 패턴 분석 + 개선 제안세션 종료 시 transcript에서 사용자 프롬프트를 추출해 월별 JSONL 파일에 저장하는 훅입니다.
#!/usr/bin/env node
/**
* prompt-logger.mjs — Stop 훅
* 세션 종료 시 transcript에서 사용자 프롬프트를 추출해
* ~/.claude/prompt-log/{YYYY-MM}.jsonl에 append한다.
*/
import fs from 'node:fs'
import path from 'node:path'
import os from 'node:os'
function readStdin() {
try { return fs.readFileSync(0, 'utf8') } catch { return '' }
}
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
/** content 블록에서 텍스트 추출 */
function textOf(content) {
if (typeof content === 'string') return content
if (Array.isArray(content)) {
return content
.filter(b => b && b.type === 'text' && b.text)
.map(b => b.text)
.join(' ')
}
return ''
}
/** tool_result만으로 구성된 메시지인지 */
function isToolResultOnly(content) {
return Array.isArray(content) && content.length > 0 &&
content.every(b => b && b.type === 'tool_result')
}
/** 프롬프트 유형 분류 */
function classifyPrompt(text, isCorrection) {
if (isCorrection) return 'correction'
const t = text.trim()
if (t.endsWith('?') || /^(어떻게|왜|뭐|how|why|what)/i.test(t)) return 'question'
if (/^(응|좋아|그래|진행|ok|yes|lgtm)$/i.test(t)) return 'feedback'
return 'instruction'
}
/** 첫 N줄 추출 (AI 응답 요약용) */
function firstLines(text, n = 3) {
return text.split('\n').filter(l => l.trim()).slice(0, n).join(' ').slice(0, 300)
}
async function main() {
const payload = JSON.parse(readStdin() || '{}')
const sessionId = payload.session_id || null
const transcriptPath = payload.transcript_path
const cwd = payload.cwd || process.cwd()
if (!sessionId || !transcriptPath) return
// transcript flush 대기
for (let i = 0; i < 8; i++) {
try {
if (fs.existsSync(transcriptPath) &&
fs.statSync(transcriptPath).size > 0) break
} catch {}
await sleep(250)
}
if (!fs.existsSync(transcriptPath)) return
const project = path.basename(cwd)
// transcript 스캔
const entries = []
const txt = fs.readFileSync(transcriptPath, 'utf8').trim()
for (const line of txt.split('\n')) {
if (!line) continue
let o
try { o = JSON.parse(line) } catch { continue }
const msg = o && o.message
if (!msg) continue
if (o.type === 'user' && msg.role === 'user'
&& !isToolResultOnly(msg.content)) {
const t = textOf(msg.content).trim()
if (t && !t.startsWith('<system')
&& !t.startsWith('<ide_')
&& !t.startsWith('This session is being continued')) {
const isCommand = t.includes('<command-name>')
entries.push({ role: 'user', text: t,
ts: o.timestamp || null, isCommand })
}
}
if (o.type === 'assistant' && msg.role === 'assistant') {
const t = textOf(msg.content).trim()
if (t) entries.push({ role: 'assistant', text: t,
ts: o.timestamp || null })
}
}
// 프롬프트-결과 쌍 + 교정 패턴 감지
const prompts = []
for (let i = 0; i < entries.length; i++) {
if (entries[i].role !== 'user') continue
const userText = entries[i].text
// 다음 assistant 응답 찾기
let resultSummary = ''
for (let j = i + 1; j < entries.length; j++) {
if (entries[j].role === 'assistant') {
resultSummary = firstLines(entries[j].text)
break
}
}
// 교정 감지: 다음 entry도 user이면 현재가 교정을 유발함
let followedByCorrection = false
for (let j = i + 1; j < entries.length; j++) {
if (entries[j].role === 'user') {
followedByCorrection = true; break
}
if (entries[j].role === 'assistant') break
}
// 이전 user 직후 → 교정 프롬프트 (슬래시 커맨드 제외)
let isCorrection = false
if (i > 0 && !entries[i].isCommand) {
for (let j = i - 1; j >= 0; j--) {
if (entries[j].role === 'user' && !entries[j].isCommand) {
isCorrection = true; break
}
if (entries[j].role === 'assistant') break
}
}
prompts.push({
ts: entries[i].ts || new Date().toISOString(),
session: sessionId,
project,
prompt: userText,
word_count: userText.split(/\s+/).length,
type: classifyPrompt(userText, isCorrection),
followed_by_correction: followedByCorrection,
result_summary: resultSummary,
})
}
if (prompts.length === 0) return
// 월별 JSONL에 append (세션별 중복 방지)
const logDir = path.join(os.homedir(), '.claude', 'prompt-log')
try { fs.mkdirSync(logDir, { recursive: true }) } catch {}
const now = new Date()
const monthFile = path.join(logDir,
`${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}.jsonl`)
// dedup: 이 세션이 이미 기록되었으면 skip
let existingSessions = new Set()
try {
const existing = fs.readFileSync(monthFile, 'utf8')
for (const line of existing.split('\n')) {
if (!line) continue
try {
const o = JSON.parse(line)
if (o.session) existingSessions.add(o.session)
} catch {}
}
} catch {}
if (existingSessions.has(sessionId)) return
const lines = prompts.map(p => JSON.stringify(p)).join('\n') + '\n'
fs.appendFileSync(monthFile, lines, 'utf8')
}
main().catch(() => {}).finally(() => process.exit(0))
훅 파일을 만들었으면 Claude Code가 실행하도록 settings.json에 등록해야 합니다.
{
"hooks": {
"Stop": [
{
"hooks": [{
"type": "command",
"command": "node ~/.claude/hooks/prompt-logger.mjs"
}]
}
]
}
}
"Stop" 배열에 추가하세요.
덮어쓰면 기존 훅이 사라집니다.
/프롬프트분석 슬래시 커맨드를 만들어 축적된 데이터를 분석합니다.
---
description: 프롬프트 로그를 분석해 패턴 발견 + 개선 제안
---
`~/.claude/prompt-log/` 디렉토리의 JSONL 파일을 분석한다.
분석 기간: $ARGUMENTS (숫자 = 최근 N개월, 비어있으면 1개월)
## 분석 관점
### 1. 기본 통계
- 총 프롬프트 수, 유형별 분포 (instruction/question/correction/feedback)
- 평균 단어 수, 가장 긴/짧은 프롬프트
### 2. 교정 패턴 분석 (가장 중요)
- `followed_by_correction: true`인 프롬프트를 모아서 공통점 파악
- "어떤 유형의 지시가 교정을 유발하는가?" - 구체적 예시와 함께 분석
- 교정률 추이 (시간 경과에 따른 변화)
### 3. 효과적 프롬프트 패턴
- 1회에 원하는 결과가 나온 프롬프트의 구조적 공통점 분석
### 4. 개선 제안 (3~5개)
- 교정 패턴에서 발견한 약점 기반 구체적 개선 팁
## 출력
결과를 `~/.claude/prompt-log/insights.md`에 저장한다.
# 최근 1개월 분석
/프롬프트분석
# 최근 3개월 분석
/프롬프트분석 3
# 이번 달 로그 확인
cat ~/.claude/prompt-log/2025-01.jsonl | head -5 | python3 -m json.tool
# 총 프롬프트 수
wc -l ~/.claude/prompt-log/*.jsonl
핵심 아이디어: AI 응답 없이 사용자가 연속으로 메시지를 보내면, 이전 결과가 불만족스러웠다는 신호입니다.
| 감지 대상 | 분류 | 의미 |
|---|---|---|
followed_by_correction: true |
교정 유발 | 이 프롬프트의 결과가 불만족 → 다음에 교정 발생 |
type: "correction" |
교정 프롬프트 | 이전 결과를 바로잡기 위해 보낸 메시지 |
type: "instruction" |
지시 | 일반적인 작업 요청 |
type: "question" |
질문 | 정보 요청 ("왜?", "어떻게?") |
type: "feedback" |
피드백 | 단순 승인 ("좋아", "진행") |
월별 JSONL 파일에 한 줄씩 저장됩니다.
{
"ts": "2025-01-15T09:30:00.000Z",
"session": "abc123-session-id",
"project": "my-app",
"prompt": "settings.json에 Stop 훅 추가해줘",
"word_count": 5,
"type": "instruction",
"followed_by_correction": false,
"result_summary": "Stop 훅을 추가했습니다. 세션 종료 시..."
}
| 필드 | 설명 |
|---|---|
ts | 프롬프트 타임스탬프 (ISO 8601) |
session | 세션 ID (중복 방지 키) |
project | 작업 디렉토리명 |
prompt | 사용자 프롬프트 전문 |
word_count | 단어 수 |
type | instruction / question / correction / feedback |
followed_by_correction | 이 프롬프트 후 교정이 발생했는가 |
result_summary | AI 응답 요약 (첫 3줄, 300자) |
2주간 716개 프롬프트를 수집한 실제 결과입니다.
| 항목 | 값 |
|---|---|
| 총 프롬프트 | 716개 |
| instruction | 492개 (68.7%) |
| question | 183개 (25.6%) |
| 실제 교정률 | 1.2% (오분류 제외) |
"확인해줘" → AI가 확인만 하고 끝냄"확인하고 수정까지 해줘""이건 됐고 다음 작업..." → AI가 이전 맥락 인용"이전 작업 완료. 새 주제: DEV-1234" 명시적 전환
"settings.json에 Stop 훅 추가해줘"1~2주마다 /프롬프트분석 실행. 교정률 추이를 보면 AI와의 소통이 개선되는지 확인할 수 있습니다.
같은 교정이 반복되면 프롬프트 습관을 바꿔야 한다는 신호입니다. 예: "확인해줘"를 항상 교정한다면 → "확인하고 수정해줘"로 습관 변경.
데이터에 project 필드가 있으므로 프로젝트별 교정률을 비교할 수 있습니다.
특정 프로젝트에서 교정률이 높다면 CLAUDE.md에 맥락을 보강하세요.
insights.md를 팀과 공유하면 "효과적인 프롬프트 가이드"가 됩니다. 교정 패턴 TOP 3 + 효과적 패턴 TOP 3만 공유해도 충분합니다.
~/.claude/
hooks/
prompt-logger.mjs # 수집 훅
commands/
프롬프트분석.md # 분석 커맨드
prompt-log/
2025-01.jsonl # 월별 수집 데이터
2025-02.jsonl
insights.md # 분석 결과
.last-analysis # 마지막 분석 시점