Initial commit: Novel Agent setup
- Add 3 AI agents (writing, revision, story-continuity specialists) - Add 4 slash commands (rovel.create, write, complete, seed) - Add novel creation/writing rules - Add Novelpia reference data (115 works, 3328 chapters) - Add CLAUDE.md and README.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
453
.claude/commands/rovel.seed.md
Normal file
453
.claude/commands/rovel.seed.md
Normal file
@@ -0,0 +1,453 @@
|
||||
---
|
||||
description: 웹소설 챕터를 씬/라인 데이터로 변환하여 D1 시드 SQL 생성
|
||||
---
|
||||
|
||||
## User Input
|
||||
|
||||
```text
|
||||
$ARGUMENTS
|
||||
```
|
||||
|
||||
User input **must** be considered (if not empty).
|
||||
|
||||
## Overview
|
||||
|
||||
마크다운 원고를 인터랙티브 씬/라인 데이터로 변환하여 D1 시드 SQL을 생성합니다.
|
||||
|
||||
**실행 방법:**
|
||||
- `/rovel.seed content/rovel/협회 소속 양호선생님/chapters/001.md` (단일 파일)
|
||||
- `/rovel.seed 협회 소속 양호선생님 1화` (작품명 + 화수)
|
||||
- `/rovel.seed 협회 소속 양호선생님 1-8화` (범위)
|
||||
- `/rovel.seed 협회 소속 양호선생님 전체` (모든 챕터)
|
||||
|
||||
**워크플로우:**
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Phase 1: 입력 파싱 │
|
||||
│ - 파일 경로 또는 작품명+화수 파싱 │
|
||||
│ - 작품 폴더 및 캐릭터 ID 매핑 로드 │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Phase 2: 마크다운 파싱 │
|
||||
│ - 씬 구분 (*** 기준) │
|
||||
│ - 라인 타입 감지 (대화/나레이션/효과음 등) │
|
||||
│ - 시스템 카드 파싱 │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Phase 3: 메타데이터 추출 │
|
||||
│ - 화자 추론 (컨텍스트 + 말투 분석) │
|
||||
│ - 씬 정보 추출 (시간/장소/분위기) │
|
||||
│ - is_playable 판정 │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Phase 4: SQL 생성 │
|
||||
│ - INSERT 문 생성 │
|
||||
│ - 파일 저장 및 안내 │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: 입력 파싱
|
||||
|
||||
### 입력 유형 분석
|
||||
|
||||
```yaml
|
||||
Case A - 절대/상대 경로:
|
||||
입력: content/rovel/협회 소속 양호선생님/chapters/001.md
|
||||
해석: 해당 파일 직접 처리
|
||||
|
||||
Case B - 작품명 + 화수:
|
||||
입력: 협회 소속 양호선생님 3화
|
||||
해석: content/rovel/협회 소속 양호선생님/chapters/003.md
|
||||
|
||||
Case C - 작품명 + 범위:
|
||||
입력: 협회 소속 양호선생님 1-8화
|
||||
해석: 001.md ~ 008.md 순차 처리
|
||||
|
||||
Case D - 작품명 + 전체:
|
||||
입력: 협회 소속 양호선생님 전체
|
||||
해석: chapters/ 폴더의 모든 .md 파일
|
||||
```
|
||||
|
||||
### 캐릭터 ID 매핑 로드
|
||||
|
||||
작품 폴더의 `인물목록.md`를 읽어 캐릭터 이름 → ID 매핑 생성:
|
||||
|
||||
```yaml
|
||||
매핑 생성 규칙:
|
||||
- 주인공 (한시우): char-{영문이름}-001
|
||||
- 히로인/주요 인물: char-{영문이름}-001
|
||||
- 조연: char-{영문이름}-001
|
||||
|
||||
예시 (협회 소속 양호선생님):
|
||||
한시우: char-hansiuw-001 (주인공)
|
||||
자화연: char-jahwayeon-001
|
||||
박준혁: char-parkjunhyuk-001
|
||||
김 과장: char-kimgwajang-001
|
||||
루나: char-luna-001
|
||||
```
|
||||
|
||||
### ID 체계
|
||||
|
||||
```yaml
|
||||
작품 ID: work-{작품영문코드}-001
|
||||
예: work-yanghosam-001
|
||||
|
||||
챕터 ID: chapter-{3자리화수}
|
||||
예: chapter-001, chapter-008
|
||||
|
||||
씬 ID: scene-{3자리화수}-{2자리순서}
|
||||
예: scene-001-01, scene-008-03
|
||||
|
||||
라인 ID: line-{3자리화수}-{2자리씬순서}-{3자리라인순서}
|
||||
예: line-001-01-001, line-008-03-015
|
||||
|
||||
시스템 카드 ID: syscard-{3자리화수}-{2자리씬순서}-{2자리순서}
|
||||
예: syscard-001-02-01
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: 마크다운 파싱
|
||||
|
||||
### 라인 타입 감지 규칙
|
||||
|
||||
```typescript
|
||||
// 우선순위 순서로 적용
|
||||
1. 빈 줄만 있음 → skip
|
||||
2. `***` → divider (씬 구분)
|
||||
3. `[제목]` 로 시작 (단, [다음 제외) → system (시스템 카드 시작)
|
||||
4. `- ` 로 시작 → sfx (효과음)
|
||||
5. `"` 로 시작하고 `"` 로 끝남 → dialogue (대화)
|
||||
6. `'` 로 시작하고 `'` 로 끝남 → thought (속마음)
|
||||
7. 그 외 → narration (나레이션)
|
||||
```
|
||||
|
||||
### 시스템 카드 파싱
|
||||
|
||||
```markdown
|
||||
입력:
|
||||
[진맥 판독]
|
||||
[대상: 박준혁, 28세, B급 헌터]
|
||||
[외상: 좌측 어깨 베임 (깊이 1.2cm)]
|
||||
[내상: 마력 과다 사용으로 인한 피로 누적]
|
||||
|
||||
출력:
|
||||
{
|
||||
type: "diagnosis",
|
||||
title: "진맥 판독",
|
||||
content: [
|
||||
{ label: "대상", value: "박준혁, 28세, B급 헌터" },
|
||||
{ label: "외상", value: "좌측 어깨 베임 (깊이 1.2cm)" },
|
||||
{ label: "내상", value: "마력 과다 사용으로 인한 피로 누적" }
|
||||
],
|
||||
summary: "대상: 박준혁",
|
||||
isPlayable: true
|
||||
}
|
||||
```
|
||||
|
||||
### 시스템 카드 타입 매핑
|
||||
|
||||
| 제목 키워드 | 타입 | isPlayable |
|
||||
|------------|------|------------|
|
||||
| 진맥 판독, 분석, 스캔 | diagnosis | true |
|
||||
| 잠재력 분석 | potential | true |
|
||||
| 환자 심리 분석, 환자 상태 | psychology | true |
|
||||
| 은밀 치유 | skill_active | true |
|
||||
| 신규 기능 감지, 잠재 해방 | skill_unlock | false |
|
||||
| 경고, 숨겨진 | warning | false |
|
||||
| 일일 진료 현황, 권장 행동 | info_update | false |
|
||||
| 그 외 | info_update | false |
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: 메타데이터 추출
|
||||
|
||||
### 화자 추론 규칙
|
||||
|
||||
```yaml
|
||||
1. 이전 3줄 컨텍스트 분석:
|
||||
"자화연이 고개를 들었다."
|
||||
"......" → speaker: char-jahwayeon-001
|
||||
|
||||
2. 대사 내 특징적 표현:
|
||||
"본좌" → 자화연
|
||||
"선생님" → 박준혁 (주인공에게 말할 때)
|
||||
|
||||
3. 주인공 여부:
|
||||
- 내면 독백 ('생각') → isProtagonist: true
|
||||
- "나는", "내가" 주어 → isProtagonist: true
|
||||
```
|
||||
|
||||
### 씬 메타데이터 추출
|
||||
|
||||
```yaml
|
||||
시간 감지:
|
||||
- "밤 11시", "새벽 2시", "오후 5시" 등
|
||||
- 첫 번째 매칭 사용
|
||||
|
||||
장소 감지:
|
||||
- "의무실", "협회", "던전" 등 키워드
|
||||
- 씬 첫 부분에서 추출
|
||||
|
||||
분위기 감지:
|
||||
- "살기", "긴장", "평온", "따뜻" 등
|
||||
- 시스템 카드나 나레이션에서 추출
|
||||
```
|
||||
|
||||
### is_playable 판정
|
||||
|
||||
```yaml
|
||||
씬이 플레이 가능한 경우:
|
||||
1. isPlayable: true인 시스템 카드가 1개 이상
|
||||
2. 주요 캐릭터와의 대화 장면
|
||||
3. 선택지가 제시될 수 있는 상황
|
||||
|
||||
is_playable: true 조건:
|
||||
- diagnosis, potential, psychology, skill_active 타입 카드 존재
|
||||
- 주요 히로인과 첫 대면 장면
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: SQL 생성
|
||||
|
||||
### 출력 파일
|
||||
|
||||
```yaml
|
||||
단일 파일:
|
||||
입력: 001.md
|
||||
출력: 001-seed.sql
|
||||
|
||||
범위/전체:
|
||||
입력: 1-8화
|
||||
출력: chapters-seed.sql (병합된 파일)
|
||||
```
|
||||
|
||||
### SQL 템플릿
|
||||
|
||||
```sql
|
||||
-- Chapter chapter-{NNN} scenes and lines
|
||||
-- Auto-generated from markdown
|
||||
|
||||
-- Delete existing data for this chapter
|
||||
DELETE FROM system_cards WHERE line_id IN (SELECT id FROM lines WHERE scene_id IN (SELECT id FROM scenes WHERE chapter_id = 'chapter-{NNN}'));
|
||||
DELETE FROM lines WHERE scene_id IN (SELECT id FROM scenes WHERE chapter_id = 'chapter-{NNN}');
|
||||
DELETE FROM scenes WHERE chapter_id = 'chapter-{NNN}';
|
||||
|
||||
-- 챕터가 없으면 생성
|
||||
INSERT OR IGNORE INTO chapters (id, work_id, number, title, is_free, price, status, view_count, created_at, updated_at)
|
||||
VALUES (
|
||||
'chapter-{NNN}',
|
||||
'{work_id}',
|
||||
{number},
|
||||
'{title}',
|
||||
{is_free},
|
||||
{price},
|
||||
'published',
|
||||
0,
|
||||
{timestamp},
|
||||
{timestamp}
|
||||
);
|
||||
|
||||
-- Scene 1: {title}
|
||||
INSERT INTO scenes (id, chapter_id, "order", title, location, time, mood, is_playable, play_description, character_ids, created_at) VALUES (
|
||||
'scene-{NNN}-{NN}',
|
||||
'chapter-{NNN}',
|
||||
{order},
|
||||
'{title}',
|
||||
'{location}',
|
||||
'{time}',
|
||||
'{mood}',
|
||||
{is_playable},
|
||||
'{play_description}',
|
||||
'{character_ids_json}',
|
||||
{timestamp}
|
||||
);
|
||||
|
||||
-- Lines for scene {N}
|
||||
INSERT INTO lines (id, scene_id, "order", type, content, speaker_id, is_protagonist, created_at) VALUES
|
||||
('line-{NNN}-{NN}-{NNN}', 'scene-{NNN}-{NN}', {order}, '{type}', '{content}', {speaker_id}, {is_protagonist}, {timestamp}),
|
||||
...;
|
||||
|
||||
-- System cards for scene {N}
|
||||
INSERT INTO system_cards (id, line_id, type, title, content, summary, is_playable, choice_label, created_at) VALUES
|
||||
('syscard-{NNN}-{NN}-{NN}', 'line-{NNN}-{NN}-{NNN}', '{type}', '{title}', '{content_json}', '{summary}', {is_playable}, '{choice_label}', {timestamp});
|
||||
```
|
||||
|
||||
### SQL 이스케이프 규칙
|
||||
|
||||
```yaml
|
||||
작은따옴표: ' → ''
|
||||
줄바꿈: \n → 그대로 (TEXT 필드)
|
||||
JSON: 쌍따옴표 사용, 이스케이프
|
||||
NULL: speaker_id 없으면 NULL (문자열 'NULL' 아님)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 실행 예시
|
||||
|
||||
### 예시 1: 단일 파일
|
||||
|
||||
```
|
||||
/rovel.seed content/rovel/협회 소속 양호선생님/chapters/008.md
|
||||
```
|
||||
|
||||
**출력:**
|
||||
```
|
||||
## 씬 변환 완료
|
||||
|
||||
**파일**: `content/rovel/협회 소속 양호선생님/chapters/008.md`
|
||||
**챕터 ID**: chapter-008
|
||||
|
||||
### 생성된 데이터
|
||||
|
||||
| 항목 | 개수 |
|
||||
|------|------|
|
||||
| 씬 | 12개 |
|
||||
| 라인 | 156개 |
|
||||
| 시스템 카드 | 3개 |
|
||||
| 플레이 가능 씬 | 2개 |
|
||||
|
||||
### 등장 캐릭터
|
||||
|
||||
| 캐릭터 | ID | 대사 수 |
|
||||
|--------|-----|---------|
|
||||
| 루나 | char-luna-001 | 24 |
|
||||
| 한시우 | (주인공) | 18 |
|
||||
|
||||
### 생성된 SQL
|
||||
|
||||
**파일**: `content/rovel/협회 소속 양호선생님/chapters/008-seed.sql`
|
||||
|
||||
### 다음 단계
|
||||
|
||||
1. SQL 확인:
|
||||
cat content/rovel/협회 소속 양호선생님/chapters/008-seed.sql
|
||||
|
||||
2. 로컬 D1 적용:
|
||||
npx wrangler d1 execute rovel-db --local --file=content/rovel/협회 소속 양호선생님/chapters/008-seed.sql
|
||||
|
||||
3. 원격 D1 적용:
|
||||
npx wrangler d1 execute rovel-db --remote --file=content/rovel/협회 소속 양호선생님/chapters/008-seed.sql
|
||||
```
|
||||
|
||||
### 예시 2: 범위 변환
|
||||
|
||||
```
|
||||
/rovel.seed 협회 소속 양호선생님 1-8화
|
||||
```
|
||||
|
||||
**출력:**
|
||||
```
|
||||
## 씬 변환 완료 (8개 챕터)
|
||||
|
||||
### 변환 결과
|
||||
|
||||
| 화수 | 씬 | 라인 | 시스템 카드 | 플레이 가능 |
|
||||
|------|-----|------|-------------|-------------|
|
||||
| 1화 | 12 | 89 | 2 | 1 |
|
||||
| 2화 | 10 | 102 | 3 | 2 |
|
||||
| ... | | | | |
|
||||
| 8화 | 14 | 156 | 3 | 2 |
|
||||
| **합계** | **92** | **1,024** | **18** | **12** |
|
||||
|
||||
### 생성된 SQL
|
||||
|
||||
**파일**: `content/rovel/협회 소속 양호선생님/chapters-seed.sql`
|
||||
|
||||
### 다음 단계
|
||||
|
||||
로컬 D1 적용:
|
||||
npx wrangler d1 execute rovel-db --local --file=content/rovel/협회 소속 양호선생님/chapters-seed.sql
|
||||
|
||||
원격 D1 적용:
|
||||
npx wrangler d1 execute rovel-db --remote --file=content/rovel/협회 소속 양호선생님/chapters-seed.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 캐릭터 ID 매핑 (협회 소속 양호선생님)
|
||||
|
||||
```yaml
|
||||
주인공:
|
||||
한시우: char-hansiuw-001
|
||||
|
||||
히로인:
|
||||
자화연: char-jahwayeon-001
|
||||
루나: char-luna-001
|
||||
|
||||
조연:
|
||||
박준혁: char-parkjunhyuk-001
|
||||
김 과장: char-kimgwajang-001
|
||||
김수진: char-kimsujin-001
|
||||
```
|
||||
|
||||
### 새 캐릭터 등장 시
|
||||
|
||||
인물목록.md에 없는 새 캐릭터가 등장하면:
|
||||
1. 경고 메시지 출력
|
||||
2. 임시 ID 생성 (`char-{이름영문}-temp`)
|
||||
3. 인물목록.md 업데이트 권장
|
||||
|
||||
---
|
||||
|
||||
## Reference Files
|
||||
|
||||
| 유형 | 경로 | 용도 |
|
||||
|------|------|------|
|
||||
| 변환 가이드 | `rules/chapter-to-scene.md` | 마크다운 문법 참조 |
|
||||
| 인물목록 | `content/rovel/{작품명}/인물목록.md` | 캐릭터 ID 매핑 |
|
||||
| 기존 시드 | `scripts/seed-d1.sql` | SQL 형식 참조 |
|
||||
| DB 스키마 | `src/server/db/schema.ts` | 테이블 구조 참조 |
|
||||
|
||||
---
|
||||
|
||||
## Important Notes
|
||||
|
||||
### 변환 시 주의사항
|
||||
|
||||
```yaml
|
||||
1. 캐릭터 ID 매핑:
|
||||
- 인물목록.md 먼저 확인
|
||||
- 없는 캐릭터는 경고 후 임시 ID
|
||||
|
||||
2. 시스템 카드:
|
||||
- [제목] 다음 줄들이 필드
|
||||
- 빈 줄이나 다른 타입 만나면 종료
|
||||
- JSON 형식으로 content 저장
|
||||
|
||||
3. 화자 추론:
|
||||
- 이전 컨텍스트 우선
|
||||
- 말투 패턴으로 보조
|
||||
- 불확실하면 NULL
|
||||
|
||||
4. SQL 이스케이프:
|
||||
- 작은따옴표는 ''로
|
||||
- NULL은 문자열 아닌 키워드
|
||||
```
|
||||
|
||||
### 스키마 호환성
|
||||
|
||||
```yaml
|
||||
현재 스키마:
|
||||
- scenes.is_playable: 플레이 가능 여부
|
||||
- scenes.character_ids: JSON 배열
|
||||
- system_cards: lines.id로 연결
|
||||
|
||||
체크 필요:
|
||||
- works 테이블에 작품 존재 여부
|
||||
- characters 테이블에 캐릭터 존재 여부
|
||||
```
|
||||
|
||||
### 작품별 설정
|
||||
|
||||
새 작품 추가 시:
|
||||
1. 작품 ID 결정 (`work-{코드}-001`)
|
||||
2. 캐릭터 ID 매핑 정의
|
||||
3. 인물목록.md에 캐릭터 정보 확인
|
||||
Reference in New Issue
Block a user