feat: ship v0.8.0 with writing logs and AI knowledge extraction

Deliver P5.2 writing day logs, cockpit heatmap, streak tracking, and chapter knowledge auto-extraction with merge review.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-07 18:26:29 +08:00
parent f331ddae86
commit a39e06ff07
51 changed files with 1436 additions and 89 deletions
@@ -0,0 +1,45 @@
import type { ExtractedKnowledgeItem } from '../../shared/types'
export function buildExtractionPrompt(
chapterTitle: string,
plainText: string,
approvedTitles: Array<{ id: string; title: string }>
): string {
const catalog =
approvedTitles.length > 0
? approvedTitles.map((e) => `- ${e.id}: ${e.title}`).join('\n')
: '(无)'
return `你是小说知识库助手。请从以下章节正文中抽取知识条目,输出 JSON 数组。
章节标题:${chapterTitle}
已有 approved 知识(id: 标题):
${catalog}
若某条抽取内容是对已有知识的更新,请设置 suggestedMergeId 为对应 id,并在 proposedPatch 中给出建议更新的字段。
每条必须包含:typecharacter|foreshadow|relationship|location|fact)、title、content、importance(1-5)、confidence(0-1)、foreshadowStatus(仅 foreshadow 类型:buried|partial|resolved)。
正文:
${plainText}
仅返回 JSON 数组,不要 markdown。`
}
export function parseExtractionJson(raw: string): ExtractedKnowledgeItem[] {
const cleaned = raw.replace(/```json\n?|\n?```/g, '').trim()
const arr = JSON.parse(cleaned) as ExtractedKnowledgeItem[]
if (!Array.isArray(arr)) throw new Error('expected array')
return arr
.map((item) => ({
type: item.type,
title: String(item.title).trim(),
content: String(item.content ?? '').trim(),
importance: item.importance ?? 3,
confidence: Number(item.confidence) || 0.5,
foreshadowStatus: item.foreshadowStatus,
suggestedMergeId: item.suggestedMergeId ?? undefined,
proposedPatch: item.proposedPatch
}))
.filter((x) => x.title.length > 0)
}