oclaw/ops/memory.py
oliver ba3836f00f 初始化:独立 oclaw 仓库首提交
- 在 oclaw/ 下重新初始化 Git 仓库
- 补齐子仓库 .gitignore,避免提交本地运行态数据(_local、node_modules、logs 等)
- 提交当前工程代码与配置

Made-with: Cursor
2026-04-24 22:31:22 +08:00

162 lines
6.7 KiB
Python

from __future__ import annotations
import argparse
import datetime as dt
from pathlib import Path
def _now_date() -> dt.date:
return dt.date.today()
def _iso_week_label(day: dt.date) -> str:
year, week, _weekday = day.isocalendar()
return f"{year}-W{week:02d}"
def _ensure_dirs(base_dir: Path) -> None:
for name in ("Projects", "Areas", "Resources", "Archives", "Inbox", "runs"):
(base_dir / name).mkdir(parents=True, exist_ok=True)
def _count_notes(path: Path) -> int:
if not path.exists():
return 0
count = 0
for item in path.rglob("*.md"):
if item.name.lower() == "readme.md":
continue
if "templates" in item.parts or "runs" in item.parts:
continue
count += 1
return count
def _recommend_new_cards(inbox_count: int, backlog_count: int, fresh_resources: int) -> int:
target = 10
if backlog_count > 0:
target -= min(6, backlog_count // 20)
if inbox_count > 20:
target -= 2
if fresh_resources >= 5 and backlog_count < 20:
target += 1
return max(3, min(10, target))
def _build_focus_order(projects: int, areas: int, resources: int) -> list[str]:
ranked = [("Projects", projects * 3), ("Areas", areas * 2), ("Resources", resources)]
ranked.sort(key=lambda x: x[1], reverse=True)
return [name for name, score in ranked if score > 0][:3]
def _parse_date(text: str | None) -> dt.date:
if not text:
return _now_date()
return dt.date.fromisoformat(text)
def _cmd_memory_status(args: argparse.Namespace) -> int:
base = Path(args.base_dir).resolve()
_ensure_dirs(base)
projects = _count_notes(base / "Projects")
areas = _count_notes(base / "Areas")
resources = _count_notes(base / "Resources")
archives = _count_notes(base / "Archives")
inbox = _count_notes(base / "Inbox")
suggested = _recommend_new_cards(inbox, int(args.review_backlog), resources)
focus = _build_focus_order(projects, areas, resources)
print(f"base_dir={base}")
print(f"projects={projects} areas={areas} resources={resources} archives={archives} inbox={inbox}")
print(f"suggested_new_cards_per_day={suggested}")
print("focus_topics=" + (",".join(focus) if focus else "Resources"))
return 0
def _cmd_memory_daily(args: argparse.Namespace) -> int:
base = Path(args.base_dir).resolve()
_ensure_dirs(base)
day = _parse_date(args.date)
projects = _count_notes(base / "Projects")
areas = _count_notes(base / "Areas")
resources = _count_notes(base / "Resources")
inbox = _count_notes(base / "Inbox")
suggested = _recommend_new_cards(inbox, int(args.review_backlog), resources)
focus = _build_focus_order(projects, areas, resources)
focus_topic = args.focus or (focus[0] if focus else "Resources")
out = base / "runs" / f"daily-{day.isoformat()}.md"
text = (
f"# Daily Memory Run ({day.isoformat()})\n\n"
f"- Focus topic: `{focus_topic}`\n"
f"- Suggested new cards today: `{suggested}`\n"
f"- Current inbox notes: `{inbox}`\n"
f"- Review backlog input: `{int(args.review_backlog)}`\n\n"
"## Auto Steps\n\n"
"1. Capture 3-5 high-value items into `Inbox`.\n"
"2. Distill 1-3 items into atomic notes under `Resources`.\n"
"3. Convert notes into Q/A or cloze cards (respect suggested limit).\n"
"4. Produce one output (summary/answer/code note).\n\n"
"## Smart Suggestions\n\n"
f"- Priority order now: `{', '.join(focus) if focus else 'Resources'}`\n"
"- If reviews feel overloaded, reduce new cards before skipping due reviews.\n"
"- If inbox keeps growing for 2+ days, process inbox first before new captures.\n"
)
out.write_text(text, encoding="utf-8")
print(f"created={out}")
return 0
def _cmd_memory_weekly(args: argparse.Namespace) -> int:
base = Path(args.base_dir).resolve()
_ensure_dirs(base)
day = _parse_date(args.date)
week_label = _iso_week_label(day)
projects = _count_notes(base / "Projects")
areas = _count_notes(base / "Areas")
resources = _count_notes(base / "Resources")
archives = _count_notes(base / "Archives")
inbox = _count_notes(base / "Inbox")
suggested = _recommend_new_cards(inbox, int(args.review_backlog), resources)
focus = _build_focus_order(projects, areas, resources)
out = base / "runs" / f"weekly-{week_label}.md"
text = (
f"# Weekly Memory Review ({week_label})\n\n"
f"- Run date: `{day.isoformat()}`\n"
f"- Suggested next-week new cards/day: `{suggested}`\n\n"
"## Snapshot\n\n"
f"- Projects notes: `{projects}`\n"
f"- Areas notes: `{areas}`\n"
f"- Resources notes: `{resources}`\n"
f"- Archives notes: `{archives}`\n"
f"- Inbox notes: `{inbox}`\n\n"
"## Fixed Review Checklist\n\n"
"- [ ] Inbox zero\n- [ ] Linking/backlinks completion\n- [ ] Move inactive notes to Archives\n- [ ] Tune next-week card load\n\n"
"## Auto Focus for Next Week\n\n"
f"- Top topics: `{', '.join(focus) if focus else 'Resources'}`\n"
"- Keep new cards within 3-10/day and adjust by real review pressure.\n"
)
out.write_text(text, encoding="utf-8")
print(f"created={out}")
return 0
def register_memory_parser(root_sub: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
memory = root_sub.add_parser("memory", help="Memory system automation")
memory_sub = memory.add_subparsers(dest="memory_cmd", required=True)
status = memory_sub.add_parser("status", help="Show memory workload status")
status.add_argument("--base-dir", default="oclaw/docs/memory-system", help="Memory system base directory")
status.add_argument("--review-backlog", type=int, default=0, help="Current due review backlog")
status.set_defaults(func=_cmd_memory_status)
daily = memory_sub.add_parser("daily", help="Generate today's automated memory run")
daily.add_argument("--base-dir", default="oclaw/docs/memory-system", help="Memory system base directory")
daily.add_argument("--date", default=None, help="Run date in YYYY-MM-DD")
daily.add_argument("--focus", default=None, help="Override focus topic")
daily.add_argument("--review-backlog", type=int, default=0, help="Current due review backlog")
daily.set_defaults(func=_cmd_memory_daily)
weekly = memory_sub.add_parser("weekly", help="Generate weekly review run sheet")
weekly.add_argument("--base-dir", default="oclaw/docs/memory-system", help="Memory system base directory")
weekly.add_argument("--date", default=None, help="Run date in YYYY-MM-DD")
weekly.add_argument("--review-backlog", type=int, default=0, help="Current due review backlog")
weekly.set_defaults(func=_cmd_memory_weekly)