import type { Register } from 'claude-code' import type { CharacterCode } from './characters' import { resolveConfig } from './config' import { indexPath, loadIndex } from './index-loader' import { buildInjection } from './inject' import type { IndexEntry } from './retrieval' import { buildRules, SECTION_ID } from './rules' // 規則段落(W8)加到系統提示最後;每則使用者訊息再依功能標籤檢索角色的對白當語氣參考(W7)。 // 對白索引在使用者資料目錄(dataDir,預設 ~/.claude/anime-companion/<角色>/index.jsonl)。 export const register: Register = (on, options) => { const config = resolveConfig(options) const showTiming = options.showTiming === true const dataDir = typeof options.dataDir === 'string' ? options.dataDir : '' let turn = 0 // 只快取讀到內容的索引;沒讀到(還沒建置)下一輪會再試,建置完不必重開 session。 const cache = new Map() on('prompt.compose', async ($, e, next) => { const result = await next(e) const text = buildRules(config) if (text === null) return result return { sections: [...result.sections, { id: SECTION_ID, text, scope: 'session' }] } }) on('prompt.submit', async ($, e, next) => { if (e.command || e.isMeta) return next(e) const load = async (code: CharacterCode) => { const hit = cache.get(code) if (hit) return hit const path = indexPath(dataDir, await $.env.get('HOME'), code) const index = await loadIndex(p => $.fs.read(p), path) if (index.length > 0) cache.set(code, index) return index } const r = await buildInjection( { config, loadIndex: load, complete: req => $.model.complete(req), now: Date.now, turn: turn++ }, e.text, ) if (r === null) return next(e) if (showTiming) $.ui.status(`anime-companion 分類 ${r.ms}ms → ${r.fn ?? '失敗'}`) // 附加在使用者訊息之前,只增不改,不影響對話的快取。 if (r.text !== null) await $.session.append({ message: { type: 'user', content: [{ type: 'text', text: r.text }] } }) return next(e) }) }