Files
jsc-plugins/plugins/anime-companion/hooks/index-loader.ts
T
Jeffery (jiantw83)andClaude Sonnet 5.5 697ed6a0b9 feat: 亞絲娜與直葉的角色包 (#6, #7)
build_pack.py 產生對白索引(放在使用者資料目錄);render_skill.py 把人格卡渲染成
角色 skill 並檢查精選對白逐字來自索引;插件改成執行期從資料目錄讀索引。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 20:52:40 +00:00

47 lines
1.6 KiB
TypeScript

import { MAX_LINE } from './examples'
import type { IndexEntry } from './retrieval'
import { isFunctionTag } from './tags'
const DEFAULT_DIR = '.claude/anime-companion'
// 對白索引由建置流水線產生,放在使用者資料目錄(不在插件裡、不入版本控制)。
// dataDir 留空時用 ~/.claude/anime-companion;找不到家目錄就回傳 null。
export function indexPath(dataDir: string, home: string | undefined, code: string): string | null {
const base = dataDir.trim() !== '' ? dataDir.trim() : home ? `${home}/${DEFAULT_DIR}` : null
return base === null ? null : `${base.replace(/\/+$/, '')}/${code}/index.jsonl`
}
// 一行一筆;格式不對的行略過,不讓壞掉的資料擋住載入。
export function parseIndex(text: string): IndexEntry[] {
const out: IndexEntry[] = []
for (const line of text.split('\n')) {
if (line.trim() === '') continue
try {
const e = JSON.parse(line) as Record<string, unknown>
if (
typeof e.id === 'string' &&
isFunctionTag(e.function) &&
typeof e.emotion === 'string' &&
(e.mode === null || typeof e.mode === 'string') &&
typeof e.text === 'string' &&
e.text.length > 0 &&
e.text.length <= MAX_LINE
) {
out.push({ id: e.id, function: e.function, emotion: e.emotion, mode: e.mode, text: e.text })
}
} catch {
// 略過壞行
}
}
return out
}
export async function loadIndex(read: (path: string) => Promise<string>, path: string | null): Promise<IndexEntry[]> {
if (path === null) return []
try {
return parseIndex(await read(path))
} catch {
return []
}
}