Ctrl K
ComunidadGuías…

butler/blackboard/state_io.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""state.md 元数据读写 + log.md append-only 流。"""

from __future__ import annotations

import re
from dataclasses import dataclass

from butler.blackboard import paths as bb_paths


@dataclass
class StateMeta:
    last_synced: str
    last_shift: str


_META_RE = re.compile(r"^_(last_synced|last_shift):\s*(.+?)_\s*$", re.MULTILINE)


def read_state_meta() -> StateMeta:
    """从 state.md 顶部解析 _last_synced / _last_shift。"""
    text = bb_paths.STATE_PATH.read_text(encoding="utf-8")
    matches = dict(_META_RE.findall(text))
    return StateMeta(
        last_synced=matches.get("last_synced", ""),
        last_shift=matches.get("last_shift", ""),
    )


def update_state_meta(*, last_synced: str | None = None, last_shift: str | None = None) -> None:
    """替换 state.md 中匹配的 _last_xxx_ 行;不存在则追加。"""
    state_path = bb_paths.STATE_PATH
    text = state_path.read_text(encoding="utf-8")
    if last_synced is not None:
        new_line = f"_last_synced: {last_synced}_"
        text, n = re.subn(r"^_last_synced:.*$", new_line, text, count=1, flags=re.MULTILINE)
        if n == 0:
            text = new_line + "\n" + text
    if last_shift is not None:
        new_line = f"_last_shift: {last_shift}_"
        text, n = re.subn(r"^_last_shift:.*$", new_line, text, count=1, flags=re.MULTILINE)
        if n == 0:
            text = new_line + "\n" + text
    state_path.write_text(text, encoding="utf-8")


def append_log_entry(shift_id: str, agent: str, summary: str) -> None:
    """append 一段摘要到 log.md(保留所有历史条目)。"""
    block = f"\n## {shift_id} · {agent}\n\n{summary.strip()}\n"
    with bb_paths.LOG_PATH.open("a", encoding="utf-8") as f:
        f.write(block)