import { expect, mock, test } from 'claude-code/testing' import { CHARACTERS } from './characters' const setup = (on: any, active?: string) => { mock.store(on, active ? { active } : undefined) on('command.register', () => ({ value: { name: 'x' } }) as never) on('session.start', () => ({ cwd: '/tmp' }) as never) on('command.run', () => ({ text: '' })) on('prompt.compose', () => ({ sections: [] })) } const compose = ($: any) => $.prompt.compose({ model: 'm', promptModel: 'm', surfaces: [], tools: [], outputStyle: null, traits: [] } as never) const persona = async ($: any): Promise => (await compose($)).sections.find((s: any) => s.id === 'jsc-anima:persona')?.text const start = ($: any) => $.session.start({ source: 'startup', cwd: '/tmp' } as never) test('啟動狀態跨 session 保留:下次開啟自動恢復', async ($, on) => { setup(on, 'sao-suguha') await start($) expect(await persona($)).toContain('直葉') }) test('啟動角色後寫入儲存,新 session 恢復;離開後不再恢復', async ($, on) => { setup(on) await start($) await $.command.run({ command: 'sao-asuna', args: '' } as never) await $.command.run({ command: 'anima-off', args: '' } as never) await $.command.run({ command: 'sao-suguha', args: '' } as never) await start($) expect(await persona($)).toContain('直葉') await $.command.run({ command: 'anima-off', args: '' } as never) await start($) expect(await persona($)).toBeUndefined() }) test('在 /config 關閉 persist 後不恢復', { options: { persist: false } }, async ($, on) => { setup(on, 'sao-asuna') await start($) expect(await persona($)).toBeUndefined() }) test('儲存的角色不存在時不恢復', async ($, on) => { setup(on, 'no-such-character') await start($) expect(await persona($)).toBeUndefined() }) test('微調數值軸:反映在 persona,其餘維持角色包預設,且不改動角色包', { options: { axisGentle: 2, axisHumor: 9 } }, async ($, on) => { setup(on, 'sao-asuna') const before = { ...CHARACTERS.find(c => c.slug === 'sao-asuna')!.axes } await start($) const text = (await persona($))! expect(text).toContain('溫柔 2') expect(text).toContain('幽默 9') expect(text).toContain(`傲嬌 ${before.tsundere}`) expect(CHARACTERS.find(c => c.slug === 'sao-asuna')!.axes).toEqual(before) }) test('未微調(-1)沿用角色包預設', { options: { axisGentle: -1 } }, async ($, on) => { setup(on, 'sao-asuna') await start($) expect(await persona($)).toContain(`溫柔 ${CHARACTERS.find(c => c.slug === 'sao-asuna')!.axes.gentle}`) }) test('8 個數值軸都可微調', { options: { axisGentle: 1, axisTsundere: 2, axisLively: 3, axisFormal: 4, axisTalkative: 5, axisHumor: 6, axisProactive: 7, axisExpressive: 8 }, }, async ($, on) => { setup(on, 'sao-asuna') await start($) expect(await persona($)).toContain('溫柔 1、傲嬌 2、活潑 3、正式 4、話量 5、幽默 6、主動 7、情緒外顯 8') }) test('超出 0-10 的覆蓋值被忽略', { options: { axisGentle: 11 } }, async ($, on) => { setup(on, 'sao-asuna') await start($) expect(await persona($)).toContain(`溫柔 ${CHARACTERS.find(c => c.slug === 'sao-asuna')!.axes.gentle}`) })