- 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>
13 KiB
13 KiB
description
| description |
|---|
| 웹소설 챕터를 씬/라인 데이터로 변환하여 D1 시드 SQL 생성 |
User Input
$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: 입력 파싱
입력 유형 분석
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 매핑 생성:
매핑 생성 규칙:
- 주인공 (한시우): char-{영문이름}-001
- 히로인/주요 인물: char-{영문이름}-001
- 조연: char-{영문이름}-001
예시 (협회 소속 양호선생님):
한시우: char-hansiuw-001 (주인공)
자화연: char-jahwayeon-001
박준혁: char-parkjunhyuk-001
김 과장: char-kimgwajang-001
루나: char-luna-001
ID 체계
작품 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: 마크다운 파싱
라인 타입 감지 규칙
// 우선순위 순서로 적용
1. 빈 줄만 있음 → skip
2. `***` → divider (씬 구분)
3. `[제목]` 로 시작 (단, [다음 제외) → system (시스템 카드 시작)
4. `- ` 로 시작 → sfx (효과음)
5. `"` 로 시작하고 `"` 로 끝남 → dialogue (대화)
6. `'` 로 시작하고 `'` 로 끝남 → thought (속마음)
7. 그 외 → narration (나레이션)
시스템 카드 파싱
입력:
[진맥 판독]
[대상: 박준혁, 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: 메타데이터 추출
화자 추론 규칙
1. 이전 3줄 컨텍스트 분석:
"자화연이 고개를 들었다."
"......" → speaker: char-jahwayeon-001
2. 대사 내 특징적 표현:
"본좌" → 자화연
"선생님" → 박준혁 (주인공에게 말할 때)
3. 주인공 여부:
- 내면 독백 ('생각') → isProtagonist: true
- "나는", "내가" 주어 → isProtagonist: true
씬 메타데이터 추출
시간 감지:
- "밤 11시", "새벽 2시", "오후 5시" 등
- 첫 번째 매칭 사용
장소 감지:
- "의무실", "협회", "던전" 등 키워드
- 씬 첫 부분에서 추출
분위기 감지:
- "살기", "긴장", "평온", "따뜻" 등
- 시스템 카드나 나레이션에서 추출
is_playable 판정
씬이 플레이 가능한 경우:
1. isPlayable: true인 시스템 카드가 1개 이상
2. 주요 캐릭터와의 대화 장면
3. 선택지가 제시될 수 있는 상황
is_playable: true 조건:
- diagnosis, potential, psychology, skill_active 타입 카드 존재
- 주요 히로인과 첫 대면 장면
Phase 4: SQL 생성
출력 파일
단일 파일:
입력: 001.md
출력: 001-seed.sql
범위/전체:
입력: 1-8화
출력: chapters-seed.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 이스케이프 규칙
작은따옴표: ' → ''
줄바꿈: \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 매핑 (협회 소속 양호선생님)
주인공:
한시우: char-hansiuw-001
히로인:
자화연: char-jahwayeon-001
루나: char-luna-001
조연:
박준혁: char-parkjunhyuk-001
김 과장: char-kimgwajang-001
김수진: char-kimsujin-001
새 캐릭터 등장 시
인물목록.md에 없는 새 캐릭터가 등장하면:
- 경고 메시지 출력
- 임시 ID 생성 (
char-{이름영문}-temp) - 인물목록.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
변환 시 주의사항
1. 캐릭터 ID 매핑:
- 인물목록.md 먼저 확인
- 없는 캐릭터는 경고 후 임시 ID
2. 시스템 카드:
- [제목] 다음 줄들이 필드
- 빈 줄이나 다른 타입 만나면 종료
- JSON 형식으로 content 저장
3. 화자 추론:
- 이전 컨텍스트 우선
- 말투 패턴으로 보조
- 불확실하면 NULL
4. SQL 이스케이프:
- 작은따옴표는 ''로
- NULL은 문자열 아닌 키워드
스키마 호환성
현재 스키마:
- scenes.is_playable: 플레이 가능 여부
- scenes.character_ids: JSON 배열
- system_cards: lines.id로 연결
체크 필요:
- works 테이블에 작품 존재 여부
- characters 테이블에 캐릭터 존재 여부
작품별 설정
새 작품 추가 시:
- 작품 ID 결정 (
work-{코드}-001) - 캐릭터 ID 매핑 정의
- 인물목록.md에 캐릭터 정보 확인