실시간 녹음 + 타임스탬프 메모 + STT 전사 + LLM 구조화 회의록 + PDF/MD 내보내기. CaptionService 연동, 동시 사용 충돌 방지, 긴 회의 2-pass 요약 지원. DB 2테이블, IPC 15채널, UI 3뷰(목록/녹음/상세), 12개 locale i18n.
104 lines
5.2 KiB
JSON
104 lines
5.2 KiB
JSON
{
|
|
"permissions": {
|
|
"allow": [
|
|
"Read",
|
|
"Glob",
|
|
"Grep",
|
|
"Bash(npm *)",
|
|
"Bash(npx *)",
|
|
"Bash(node *)",
|
|
"Bash(git *)",
|
|
"Bash(ls *)",
|
|
"Bash(mkdir *)",
|
|
"Bash(which *)",
|
|
"Bash(python *)",
|
|
"Bash(pip *)",
|
|
"Bash(taskkill *)",
|
|
"Bash(mv *)",
|
|
"Bash(rm *)",
|
|
"Edit(src/**)",
|
|
"Edit(tests/**)",
|
|
"Edit(docs/**)",
|
|
"Edit(scripts/**)",
|
|
"Edit(.claude/**)",
|
|
"Edit(*.md)",
|
|
"Edit(*.json)",
|
|
"Write(src/**)",
|
|
"Write(tests/**)",
|
|
"Write(docs/**)",
|
|
"Write(scripts/**)",
|
|
"Write(.claude/**)",
|
|
"Write(*.md)",
|
|
"Write(*.json)"
|
|
],
|
|
"deny": [
|
|
"Bash(rm -rf /)",
|
|
"Bash(git push --force*)",
|
|
"Bash(npm publish*)",
|
|
"Edit(.env*)",
|
|
"Edit(secrets.*)"
|
|
]
|
|
},
|
|
"hooks": {
|
|
"UserPromptSubmit": [
|
|
{
|
|
"matcher": "",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat <<'INJECT'\n[D3RO-VOICE 강제 규칙]\n1. 구현 전 반드시 설계서 참조: docs/design/00~03 (아키텍처, 서비스 명세, IPC 타입, DB/UI)\n2. 서비스: 싱글톤 + EventEmitter. 설계서 01의 인터페이스를 그대로 구현\n3. IPC: 설계서 02의 ipc-channels.ts 채널명/타입을 그대로 사용\n4. 에러: 설계서 02의 D3ROError + ErrorCode enum 사용\n5. DB: 설계서 03의 drizzle-orm 스키마를 그대로 사용\n6. UI: 설계서 03의 컴포넌트 Props/상태/IPC 명세를 그대로 따름\n7. 윈도우: 프리로딩 + 2-phase 리사이즈 (설계서 01 WindowManagerService)\n8. 상태머신: RecognitionState + AudioState 분리 (설계서 01 VoiceModeService)\n9. 팝업: Vanilla JS, 설계서 03의 HTML/CSS/애니메이션 스펙 준수\n10. any 타입 절대 금지, console.log 절대 금지\n11. 테마 SSOT: 색상은 d3roPalette/d3roShadow(CSS var) 사용, 렌더러 tsx/ts에 하드코딩 hex 금지 (theme.ts 제외)\n12. i18n: React 컴포넌트 UI 문자열은 t() 함수 사용, 하드코딩 한국어/영어 금지\n13. 작업 완료 즉시 memory/project_status.md 갱신 의무 — 미루지 말 것\nINJECT"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"PostToolUse": [
|
|
{
|
|
"matcher": "Write|Edit",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "FILE=$(cat /dev/stdin | python -c \"import sys,json; d=json.load(sys.stdin); print(d.get('tool_input',{}).get('file_path',''))\" 2>/dev/null); if echo \"$FILE\" | grep -qE '\\.(ts|tsx)$'; then cd 'D:/workspace/D3ROVoice' && npx tsc --noEmit 2>&1 | head -20 || true; fi"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"matcher": "Write|Edit",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "FILE=$(cat /dev/stdin | python -c \"import sys,json; d=json.load(sys.stdin); print(d.get('tool_input',{}).get('file_path',''))\" 2>/dev/null); if echo \"$FILE\" | grep -qE '\\.(ts|tsx)$'; then ISSUES=''; ANY=$(grep -nE ': any[^a-zA-Z]|<any>|as any' \"$FILE\" 2>/dev/null | grep -v '// eslint' | grep -v 'import' | head -3); if [ -n \"$ANY\" ]; then ISSUES=\"any 타입: $ANY\"; fi; LOG=$(grep -n 'console\\.log' \"$FILE\" 2>/dev/null | head -3); if [ -n \"$LOG\" ]; then ISSUES=\"$ISSUES | console.log: $LOG\"; fi; if [ -n \"$ISSUES\" ]; then echo \"[HARNESS VIOLATION] $ISSUES — 즉시 수정 필요\"; fi; fi"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"matcher": "Write|Edit",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "FILE=$(cat /dev/stdin | python -c \"import sys,json; d=json.load(sys.stdin); print(d.get('tool_input',{}).get('file_path',''))\" 2>/dev/null); if echo \"$FILE\" | grep -qE 'src/renderer/.*\\.(tsx|ts)$' && ! echo \"$FILE\" | grep -q 'theme\\.ts' && ! echo \"$FILE\" | grep -q 'theme-vars'; then HC=$(grep -noE '#[0-9a-fA-F]{6}' \"$FILE\" 2>/dev/null | grep -v '//' | grep -v 'var(' | head -3); if [ -n \"$HC\" ]; then echo \"[SSOT WARN] 하드코딩 hex 색상 — d3roPalette/CSS var 사용:\"; echo \"$HC\"; fi; fi"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"matcher": "Write|Edit",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "FILE=$(cat /dev/stdin | python -c \"import sys,json; d=json.load(sys.stdin); print(d.get('tool_input',{}).get('file_path',''))\" 2>/dev/null); if echo \"$FILE\" | grep -qE 'src/renderer/.*\\.tsx$' && ! echo \"$FILE\" | grep -qE 'locales/|i18n|test'; then I18N=$(python -c \"import re; lines=open('$FILE','r',encoding='utf-8').readlines(); [print(f'{i+1}:{l.rstrip()}') for i,l in enumerate(lines) if re.search(r'[\\uAC00-\\uD7AF]{2,}',l) and '//' not in l and 'import' not in l]\" 2>/dev/null | head -3); if [ -n \"$I18N\" ]; then echo \"[i18n WARN] 하드코딩 한국어 문자열 — t() 함수 사용 필요:\"; echo \"$I18N\"; fi; fi"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"PreToolUse": [
|
|
{
|
|
"matcher": "Write|Edit",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "FILE=$(cat /dev/stdin | python -c \"import sys,json; d=json.load(sys.stdin); print(d.get('tool_input',{}).get('file_path',''))\" 2>/dev/null); if echo \"$FILE\" | grep -qE '\\.env|\\.secret|credentials'; then echo 'BLOCKED: 시크릿 파일 수정 금지' >&2; exit 2; fi; exit 0"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|