feat: 補齊工具一(最新章節狀態、作品用語、語言欄位、貼上章節、台詞引號檢查)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
root
2026-10-15 08:34:36 +00:00
co-authored by Claude Sonnet 5.5
parent 8c25728206
commit fb04b0fa23
7 changed files with 89 additions and 10 deletions
+15 -4
View File
@@ -5,7 +5,7 @@ import type { CharacterPack, Conflict, Extracted, WorkPack } from './extract'
export const EXTRACT_COMMAND = 'anima-extract'
const PENDING_KEY = 'extract:pending'
const USAGE = `用法:/jsc-anima:${EXTRACT_COMMAND} <章節檔案路徑>,之後用 /jsc-anima:${EXTRACT_COMMAND} confirm 逐步確認,cancel 取消。`
const USAGE = `用法:/jsc-anima:${EXTRACT_COMMAND} <章節檔案路徑>(或 paste <章節名稱> 並於下一行起貼上內容),之後用 /jsc-anima:${EXTRACT_COMMAND} confirm 逐步確認,cancel 取消。`
type Plan = {
files: { path: string; json: unknown }[]
@@ -38,13 +38,17 @@ async function readJson<T>($: Engine, path: string): Promise<T | undefined> {
const slugKey = (c: Extracted['characters'][number]) => slugify(c.slug || c.nameEn || c.name) || c.name
async function stage($: Engine, path: string): Promise<string> {
async function stageFile($: Engine, path: string): Promise<string> {
const cwd = await $.session.cwd().catch(() => '')
const full = path.startsWith('/') ? path : `${cwd}/${path}`
if (!(await $.fs.exists(full).catch(() => false))) return `找不到檔案:${path}`
const text = String(await $.fs.read(full))
if (!text.trim()) return `檔案是空的:${path}`
return stage($, (path.split('/').pop() ?? path).replace(/\.[^.]+$/, ''), text)
}
async function stage($: Engine, chapter: string, text: string): Promise<string> {
const cwd = await $.session.cwd().catch(() => '')
const chunks = chunkText(text)
const parts: Extracted[] = []
for (const [i, chunk] of chunks.entries()) {
@@ -55,7 +59,6 @@ async function stage($: Engine, path: string): Promise<string> {
parts.push(parsed)
}
const extracted = mergeChunks(parts)
const chapter = (path.split('/').pop() ?? path).replace(/\.[^.]+$/, '')
const workSlug = slugify(extracted.work.slug || extracted.work.nameEn || extracted.work.name)
if (!workSlug) return '無法產生作品代號,請在章節中提供作品的英文名。'
const slugs: Record<string, string> = {}
@@ -172,7 +175,15 @@ export async function runExtract($: Engine, args: string): Promise<string> {
await $.store.delete(PENDING_KEY)
return '已取消,沒有寫入任何檔案。'
}
if (head !== 'confirm') return stage($, args.trim())
if (head === 'paste') {
// 貼上:第一行 paste <章節名稱>,下一行起為章節內容。
const [first = '', ...body] = args.trim().split('\n')
const name = first.replace(/^paste\s*/, '').trim()
const text = body.join('\n')
if (!name || !text.trim()) return `貼上章節的用法:/jsc-anima:${EXTRACT_COMMAND} paste <章節名稱>,下一行起貼上章節內容。`
return stage($, name, text)
}
if (head !== 'confirm') return stageFile($, args.trim())
const p = (await $.store.get(PENDING_KEY)) as Pending | undefined
if (!p) return `沒有待確認的萃取,請先執行 /jsc-anima:${EXTRACT_COMMAND} <章節檔案路徑>。`