Files
jsc-marketplace/plugins/hud/hooks/companion/todo.ts
T
jiantw83andClaude Sonnet 5.5 ac33dcca3b feat: M4 主動關心、待辦與記憶鞏固核心模組
- care:事件式關心(冷卻、每日上限、靜音、回覆中/輸入中不彈出),toast 與注入兩種輸出,台詞依觸發 × 個性 × 好感階段
- todo:新增、完成、承諾事項與到期提醒
- memory:檢索評分、遺忘曲線、鞏固與反思、金鑰過濾
- 以假時鐘與記憶體檔案系統測試核心接縫

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 13:30:22 +08:00

73 lines
2.0 KiB
TypeScript

import type { Clock, Fs } from './io'
import { readJson, writeJson } from './io'
export interface Todo {
id: number
title: string
done: boolean
/** 承諾事項:助理答應記住的事 */
promise: boolean
dueAt?: number
createdAt: number
doneAt?: number
/** 已提醒過,避免重複 */
reminded?: boolean
}
export interface TodoList {
add(title: string, opts?: { promise?: boolean; dueAt?: number }): Promise<Todo>
complete(id: number): Promise<Todo | null>
list(opts?: { all?: boolean }): Promise<Todo[]>
/** 角色行顯示的待辦數(未完成) */
open(): Promise<number>
/** 取出到期且尚未提醒的項目,並標記為已提醒 */
due(): Promise<Todo[]>
}
export function createTodos(fs: Fs, clock: Clock, path: string): TodoList {
const load = () => readJson<Todo[]>(fs, path, [])
const save = (t: Todo[]) => writeJson(fs, path, t)
return {
async add(title, opts = {}) {
const all = await load()
const t: Todo = {
id: all.reduce((m, x) => Math.max(m, x.id), 0) + 1,
title: title.trim(),
done: false,
promise: !!opts.promise,
dueAt: opts.dueAt,
createdAt: clock(),
}
all.push(t)
await save(all)
return t
},
async complete(id) {
const all = await load()
const t = all.find(x => x.id === id)
if (!t || t.done) return t ?? null
t.done = true
t.doneAt = clock()
await save(all)
return t
},
async list(opts = {}) {
const all = await load()
return opts.all ? all : all.filter(t => !t.done)
},
async open() {
return (await load()).filter(t => !t.done).length
},
async due() {
const all = await load()
const now = clock()
const hit = all.filter(t => !t.done && !t.reminded && t.dueAt !== undefined && t.dueAt <= now)
if (hit.length) {
for (const t of hit) t.reminded = true
await save(all)
}
return hit
},
}
}