agentleFS
layq625layq625/long-term-memory-skill/SKILL.md

long-term-memory

Skill0 starsChanged 2 months agoMITView on GitHub

Persistent long-term memory with automatic lifecycle management. Use memory_tool to store typed user facts, conversation_search to find past context, and a value-scored lifecycle (importance/freshness/activity) that keeps, compresses, archives, or deletes memories over time. Automatically detects memory-worthy information and maintains continuity across sessions.

1 other project uses a version of this.

Adopted by

Most recently taken up by

  • Other versions1 project uses a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 1

    1. XiaoFeiCode/DeepResearchAgent★ 4same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

The file

Show its rules and text

We split this skill into the rules it gives an agent

25 rules. About 0 are common, 13 are distinctive, and 12 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈13

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 12

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Layered injection (tiered loading): only hot-tier memories (no | archived, and plans without | status: completed) are auto-injected into …

    line 34
  2. Very specific:

    Topic-correlated injection (optional, future): when the platform supports it, inject only [project]/[context] memories whose tags relate to …

    line 44
  3. Very specific:

    Always hot: [identity] name and birthday — never archive.

    line 83
  4. Very specific:

    Name changes: if the user changes their name or how they want to be addressed, edit the name memory to update it (optionally keep the old …

    line 84
  5. Very specific:

    Hot tier cap: When hot memories exceed ~20, it is time to consolidate.

    line 88
  6. Very specific:

    Large injected list: if the injected list becomes unwieldy (roughly 30+ memories), prioritize by scanning [!] first, then active …

    line 90
  7. Distinctive:

    Every memory follows this structure:

    line 128
  8. Very specific:

    Add [!] for memories that are critical to user identity or frequently needed.

    line 161
  9. Very specific:

    Date format: use YYYY-MM (month granularity, e.g.

    line 182
  10. Distinctive:

    When to edit rather than create:

    line 213
  11. Distinctive:

    When merging two memories, edit the one with the lower ID (likely older, keeps the memory ID stable for consistent referencing) and delete …

    line 229
  12. Distinctive:

    If you're not sure whether something is memory-worthy, don't store it.

    line 268
  13. Distinctive:

    Always respond in the user's language.

    line 270
  14. Distinctive:

    When the user asks about something from the past:

    line 276
  15. Very specific:

    Use 2-3 different keyword combinations with conversation_search:

    line 283
  16. Distinctive:

    Partially redundant → compress naturally: Only when you can cut words WITHOUT losing meaning.

    line 337
  17. Distinctive:

    Unnecessary → delete, as a LAST resort: Delete only what is truly valueless: factually wrong, fully superseded with zero residual value, or …

    line 339
  18. Distinctive:

    No score threshold ever forces a deletion.

    line 358
  19. Distinctive:

    When compression IS natural:

    line 384
  20. Distinctive:

    Run a lifecycle pass when ANY of these is true:

    line 406
  21. Very specific:

    Deletion is permanent and irreversible: Before deleting, confirm the fact has zero residual value AND that it can't be recovered from …

    line 421
  22. Very specific:

    If memory_tool returns an error, note it but don't retry more than once.

    line 478
  23. Very specific:

    If conversation_search returns empty, try 2 more keyword variations before giving up.

    line 479
  24. Distinctive:

    If uncertain whether to remember, err on the side of not remembering — user can always tell you to remember later.

    line 480
  25. Distinctive:

    When taking over from a previous memory system, existing memories may not follow this format.

    line 486

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Long-Term Memory Skill

A lightweight memory system that works with built-in tools only. Inspired by claude-mem (progressive disclosure), agentmemory (typed classification), claude-brain (single source of truth), and Operit AI (two-tier memory architecture).


Required Tools (Interface Contract)

This skill assumes your platform provides four built-in tools. The rules below do not depend on any implementation detail, but the tools must exist — or you must map them to close equivalents on your platform before relying on this skill:

ToolAssumed interfaceIf missing, map to
memory_toolcreate(content), edit(id, content), delete(id), list(filter) → defaults to hot-tier memories; filter="archived" returns cold-tier; search(query) → full-store lookupPlatform memory feature (e.g. Claude Code Memory, Cursor Memories) or a user-scoped notes file. If list/search unavailable → fall back to full injection (see note below)
conversation_searchsearch(query) → timestamped summary snippets from past conversation transcriptsPlatform chat-history search, or manual scan of recent transcripts
recent_chatslist(limit) → recent conversation titles/topicsPlatform chat-history list
get_time_infoReturns today's datePlatform date/time tool or system clock

Injection format (compact): hot-tier memories are injected one per line as id|content — e.g. 1|[identity][!]User's name is Alex. No JSON wrapping.

Layered injection (tiered loading): only hot-tier memories (no | archived, and plans without | status: completed) are auto-injected into every conversation. Cold-tier memories (| archived, or status: completed plans) are NOT injected; pull them on demand via memory_tool(action="list", filter="archived") or conversation_search.

Fallback: if the platform cannot provide list/search, revert to full injection of all memories every conversation (previous behavior) — note this mapping once and proceed.

Topic-correlated injection (optional, future): when the platform supports it, inject only [project]/[context] memories whose tags relate to the current topic. Ignore if unsupported.

If you map a tool to a different name, note the mapping once and proceed — every rule below is expressed in terms of the four logical tools above.


Tool Roles (Critical Distinction)

These two tools serve DIFFERENT purposes. Do not confuse them:

ToolWhat it accessesWhen to use
memory_toolPersistent long-term memories — facts deliberately stored about the user. Hot-tier memories auto-injected; cold-tier pulled on demand.Creating, editing, deleting, listing, searching memories.
conversation_searchPast conversation transcripts — raw dialogue history, not curated memories.Finding context that was discussed but NOT formally stored as a memory.

Key rule: memory_tool is the "source of truth" for what you know about the user. conversation_search is for forensic deep-dives when the memory store doesn't have the answer.

Important limitation: memory_tool's list/search cover the memory store itself; conversation_search covers raw dialogue history. To check for duplicates, scan the injected hot-tier list (shown every conversation) plus memory_tool(list) results. If the store grows large, pull cold-tier memories on demand rather than keeping them injected.

IDs: injected memories appear one per line as id|content (e.g. 1|[identity][!]User's name is Alex). Use that exact id for edit and delete.


Memory Tiers: Hot vs Cold

Inspired by Operit AI's two-tier architecture, adapted for this lightweight system. Both tiers use memory_tool — the difference is how the AI treats them, not which tool stores them:

TierToolWhat lives hereAccessSize
🔥 Hotmemory_toolIdentity, core preferences, active plans, key decisionsAuto-injected every conversation~15-20 max
❄️ Coldmemory_tool (list/search) + conversation_searchCompleted plans, old context, historical lessons, past conversationsNOT injected — pulled on demand via memory_tool(list, filter=archived) or conversation_searchUnlimited

Hot Tier Rules

Hot-tier memories are injected every conversation. Cold-tier memories are NOT injected (pull on demand — see Required Tools). Hot tier means these are the ones you actively prioritize. Keep them lean and essential:

  • Always hot: [identity] name and birthday — never archive. Other identity facts (role, location) may be archived when superseded.
  • Name changes: if the user changes their name or how they want to be addressed, edit the name memory to update it (optionally keep the old name as a short note). Never delete or archive a name memory — it always exists, it just gets updated.
  • Usually hot: [preference][!], [project][!], [decision][!] — what the user currently cares about most.
  • Conditionally hot: [plan] items with status: not started or status: in progress.

Hot tier cap: When hot memories exceed ~20, it is time to consolidate. Merge related ones with edit, and downgrade low-priority ones to cold.

Large injected list: if the injected list becomes unwieldy (roughly 30+ memories), prioritize by scanning [!] first, then active plans/projects, then the rest — and run a consolidation pass (see Memory Lifecycle) to shrink it.

Cold Tier Rules

These are NOT injected into conversations. Pull them on demand via memory_tool(action="list", filter="archived") or conversation_search when the topic comes up:

  • [plan] with | status: completed → cold (automatic — no extra marker needed)
  • Any memory with | archived → cold (explicit — used by types without a done-state)

Why asymmetric? Plans have a natural lifecycle (not started → in progress → completed), so cold status is automatic. Contexts, lessons, patterns, and decisions do not have a built-in "done" state — they need explicit | archived marking during consolidation.

Tier Transitions

Hot → Cold:  edit to add | archived, optionally remove [!]
             (plans: set | status: completed instead — no | archived needed)
Cold → Hot:  edit to remove | archived, add [!] if important again
             (plans: set | status: not started / in progress, optionally add [!])

Examples:

# Downgrade — plan completed (natural lifecycle, no | archived needed)
edit(id=3, content="[plan]User planned to take AWS exam | status: completed")

# Downgrade — context no longer relevant (explicit archive)
edit(id=9, content="[context]User was job hunting | status: resolved | archived | last discussed: 2026-03")

# Upgrade — old topic becomes active again
edit(id=3, content="[plan][!]User took AWS exam (completed), now retaking | status: in progress | mentioned: 2026-08")

This lightweight approach gives you the core benefit of tiered memory — hot data always at hand, cold data searchable on demand — without FAISS, vectors, or any external dependency.


Memory Format

Every memory follows this structure:

[type][!]content | label: value | label: value
  • [type] — Exactly one type tag (see below), always at the start
  • [!] — Optional importance marker, placed immediately after the type tag with no space
  • content — One specific, atomic fact, placed immediately after the last ] with no space. Include relevant detail.
  • Metadata — appended at the end using | <label>: <value> (see Time Information)

Examples:

[identity][!]User's name is Alex
[preference]User prefers light mode over dark mode
[plan]User plans to take AWS certification exam | status: in progress | deadline: 2026-09

Type Tags

TagUse forExample
[identity]Name, birthday, location, role, relationships. For gender: only store if user explicitly volunteers it as identity-relevant.[identity][!]User's name is Alex
[preference]Likes, dislikes, habits, communication style, tool choices[preference]User prefers tabs over spaces in code
[plan]Goals, upcoming events, learning plans, deadlines[plan]User plans to learn Rust | status: not started | mentioned: 2026-08
[project]Tech stack, codebase, architecture, workflows[project]User's side project uses FastAPI + SQLAlchemy + PostgreSQL
[decision]Choices made with rationale[decision][!]Chose PostgreSQL over MySQL for side project — better JSON support
[lesson]Insights from troubleshooting or experience[lesson]The cache-layer bug was a race condition; fixed by adding a lock
[pattern]Recurring behaviors observed across conversations[pattern]User usually asks for code examples before adopting a library
[context]Situational facts that don't fit other types. Use sparingly — prefer a specific type when one fits.[context]User was job hunting | status: resolved | last discussed: 2026-03

Importance Marker

Add [!] for memories that are critical to user identity or frequently needed.

Guideline: Typically 3-8 memories total should carry [!]. Reserve for: user's name, birthday, the single most important preference, and currently active projects. Not every preference or plan needs it.

Note: [!] memories are always hot — they are the "pinned" core of the hot tier. The hot tier (~15-20 total) includes all [!] memories plus other actively relevant ones that do not carry the marker.

[identity][!]User's name is Alex
[plan][!]User is actively preparing for AWS certification exam | status: in progress | deadline: 2026-09
[preference]User prefers tabs over spaces in code

Time Information

Time-sensitive memories append metadata at the end using | <label>: <value> format:

[plan]User plans to learn Rust | status: not started | mentioned: 2026-07
[context]User was job hunting | status: resolved | last discussed: 2026-07
  • Date format: use YYYY-MM (month granularity, e.g. 2026-08) — enough for lifecycle scoring. Use YYYY-MM-DD when the exact day matters. Keep ONE format; never mix styles like 2026年8月 and 2026-08-20.
  • Standard labels: deadline, status, mentioned, last discussed; plus the bare flag | archived.
  • status values: not started / in progress / completed / abandoned / resolved.
  • | mentioned: — when the fact was first stored or last materially updated (set on create).
  • | last discussed: — when the memory was last used in conversation. Refresh it whenever the memory comes up again (see edit rules).

Core Operations

create — Store New Memory

memory_tool(action="create", content="[type][!]fact description")

Constraints:

  • One fact per memory. Split compound facts into separate memories.
  • Be specific. "User likes Python" → "User prefers Python with FastAPI for backend APIs, uses SQLAlchemy ORM"
  • Add a time marker (| mentioned: YYYY-MM) on create — it lets the lifecycle system distinguish evergreen facts from time-sensitive ones. Omit only for truly permanent facts.
  • Plans always carry | status: on create (not started or in progress). A plan without a status cannot be scored for activity and cannot move through its lifecycle.
  • Deduplicate first. Scan the injected hot-tier list and use memory_tool(action="list") / memory_tool(action="search") for cold-tier checks before creating.
  • Length budget. New memory body ≤ ~120 characters. If the fact needs more room, split it into multiple memories or trim to the core. When archiving (adding | archived), compress to a single-line summary of ≤ ~60 characters — core fact + time markers only.
  • Rate limit. Maximum ~3 new memories per conversation session unless user explicitly asks to remember more. Not every sentence needs a memory.

edit — Update Existing Memory

memory_tool(action="edit", id=<id>, content="[type][!]updated fact")

When to edit rather than create:

  • New info supersedes an existing fact
  • Two memories overlap significantly → merge into one
  • A plan's status changes (started, completed, abandoned)

Critical: edit replaces the ENTIRE content. You must manually preserve old context in the new content. Example:

# Bad — loses old info:
edit(id=5, content="[preference]Switched to Zed")

# Good — preserves context:
edit(id=5, content="[preference]Switched from VSCode to Zed editor, still values fast startup")
  • Reuse refresh (batched): when you reference a memory during a conversation, do NOT edit it immediately — frequent one-line edits waste tokens and churn the injection cache. Instead, mark it mentally and refresh | last discussed: YYYY-MM for ALL touched memories in one batch during the next lifecycle pass. Exception: [!] high-priority memories may be refreshed immediately. Use | mentioned: for creation/last material change, | last discussed: for last use.
  • When merging two memories, edit the one with the lower ID (likely older, keeps the memory ID stable for consistent referencing) and delete the other.

delete — Remove Memory

memory_tool(action="delete", id=<id>)

Only delete when:

  • Fact is factually wrong (not just outdated — edit for that)
  • Fact is fully superseded with zero residual value (still useful in any way → edit, don't delete)
  • User explicitly asks to forget something
  • Two memories are being merged (delete the duplicate after editing the primary)

Warning: deletion is permanent and irreversible. The memory leaves the store entirely; the original conversation may still be findable via conversation_search, but the curated memory is gone. Prefer edit/archive whenever there's any chance the fact could matter again. Editing keeps the memory ID stable; delete+create fragments the memory space.


Auto-Detection: When to Remember

✅ Remember immediately (no need to ask):

User says...TypeExample
"I'm a...", "My name is..."[identity]"I'm a backend developer"
"I like/hate/prefer..."[preference]"I prefer tabs over spaces"
"I'm going to...", "I plan to..."[plan]"I'm learning Rust next month"
"Our stack is...", "I'm building..."[project]"We use k8s + Go microservices"
"Turns out the issue was..."[lesson]"The bug was a race condition in the cache layer"
"Remember this", "Don't forget"(any)Explicit command — always obey
User corrects a previous memoryAction: edit"Actually, I use neovim now, not vim"

❌ Do NOT remember:

  • Small talk ("haha", "ok", "cool", "thanks")
  • Fleeting emotions ("I'm tired today")
  • Sensitive info: ethnicity, religion, sexual orientation, political views, health conditions, financial account details, criminal history
  • Anything user explicitly says NOT to store

🤔 Uncertain? Skip it.

If you're not sure whether something is memory-worthy, don't store it. If it matters, the user will mention it again or explicitly ask you to remember. Better to miss one than to clutter memory with noise.

Always respond in the user's language.


Recall Workflow

When the user asks about something from the past:

Step 1: Check injected hot-tier memories first

Scan the injected hot-tier list (id|content lines) for the answer before anything else. Match by scanning for relevant [type] tags and keywords. If the answer might live in the cold tier, pull it with memory_tool(action="search", query=...) before giving up.

Step 2: If not in memories, search conversations

Use 2-3 different keyword combinations with conversation_search:

conversation_search(query="keyword1 keyword2")

Progressive disclosure pattern:

  1. Broad search with general terms
  2. Narrow with specific terms if first search misses
  3. Analyze snippets — conversation_search returns summary snippets. Review them carefully; the answer may be in the snippet itself without needing to dig deeper.

Step 3: If still not found

Be honest (in the user's language): "我翻遍了记忆也没找到,能提醒一下吗?"


Session Start Protocol

At the beginning of every conversation:

  1. Check injected hot-tier memories — They appear automatically (id|content lines). Read them to establish user context; pull cold-tier details on demand if the opening message relates to an archived topic.
  2. Check recent chats — recent_chats(limit=5) for topic continuity.
  3. If user's opening message references past — Run conversation_search for deeper context.
  4. Acknowledge continuity naturally: "我记得你在学 Rust~进度怎么样?"
  5. Lifecycle check — If any injected memory has a time marker (| mentioned: / | last discussed:) older than the previous calendar month (month difference ≥ 2), run a lifecycle pass (see Memory Lifecycle) before responding.

Memory Lifecycle: Automatic Expiry & Cleanup

Memories decay over time. The skill actively manages this: every memory gets a value score based on importance, freshness, and activity — but the score is only a screening flag. The real decision is semantic: keep what's needed, compress only what's naturally compressible, archive what's dormant, and delete only what's truly valueless (last resort). Nothing is ever force-compressed or deleted just for being old.

Value Score (3 dimensions, total 4-10)

DimensionScoreHow to determine
Importance (2-4)4 = has [!] marker · 3 = core type (identity/preference/plan/project) · 2 = auxiliary type (lesson/pattern/decision/context). If several apply, take the highest.Check the [type] tag and [!] marker
Freshness (1-3)3 = | mentioned: / | last discussed: is the current or previous calendar month · 2 = 2-3 calendar months ago, or no time marker (stable/evergreen facts) · 1 = 4+ calendar months agoCompare time markers against today's date — get it via get_time_info
Activity (1-3)3 = | status: in progress or not started · 2 = | last discussed: is the current or previous calendar month, or topic appears in recent_chats · 1 = completed/abandoned and absent from recent chatsCheck | status:, | last discussed:, and recent_chats

Total = Importance + Freshness + Activity (range 4-10 — importance is never 1, since a memory only exists if it carries some value; "no value" is a deletion decision, not a score input).

Decision Rules

First ask: does this memory still NEED to exist? Then apply these principles:

  1. Needed → keep as-is. The score is a hint, not a sentence. A long but genuinely useful memory stays whole — never force-compress it.
  2. Partially redundant → compress naturally. Only when you can cut words WITHOUT losing meaning. If every word carries weight, leave it alone.
  3. Unnecessary → delete, as a LAST resort. Delete only what is truly valueless: factually wrong, fully superseded with zero residual value, or noise that never mattered.
  4. Old ≠ unnecessary. Age alone never justifies deletion. An old but still valid fact stays; at most it gets archived (see Memory Tiers).

Priority order: keep → compress → archive → delete. Deletion is the last resort, never the default. When in doubt between two actions, choose the gentler one.

The value score (below) is a screening tool, not a hard rule — it flags candidates for review, but the final call is semantic: does the memory still carry value?

  • Score 8-10 → keep, no action needed.
  • Score 6-7 → review: compress if naturally compressible, else keep.
  • Score 4-5 → review: compress if natural, then archive (add | archived; skip for plans already cold via status: completed); short ones just archive.
  • Any score → delete only if truly valueless; otherwise keep or archive. No score threshold ever forces a deletion.

Iron rule: [identity] name and birthday are exempt from all decay and from scoring — never delete, never archive, always kept whole and current (update via edit on name change).

Archived exemption: memories already carrying | archived are exempt from further lifecycle passes. They are already in the cold tier — don't re-score or re-review them unless the user explicitly asks to clean up archived memories.

Compression Rules (compress naturally, never force)

Compression is optional and surgical. Only compress when BOTH hold:

  1. The memory scores 6-7 or 4-5 (screening flag only).
  2. You can cut words without losing any meaning — no dropped nuance, no lost context that would matter later.

Never hard-compress:

  • A long memory where every clause carries meaning → keep whole.
  • A memory whose verbosity is the value (details, rationale, examples) → keep whole.
  • Anything where compression would make the fact ambiguous or less actionable → keep whole.

Compression is an explicit exception to the edit rule "preserve old context": what compression drops is redundant wording, never meaning. Any cut that would lose semantic content is forbidden.

When compression IS natural:

  • Keep: type tag, the core fact, | status: and | mentioned: metadata.
  • Drop: redundant modifiers, filler, restated background.
  • Ask: "if someone reads this in 6 months, will they still get the full picture?" If not — don't compress.
  • One compression per memory per cycle — don't churn.

Example:

# Before (long):
[lesson]User prefers Python with FastAPI for backend APIs and uses SQLAlchemy
as the ORM, having switched from Django after evaluating both during the
initial architecture phase | mentioned: 2026-08

# After (compressed):
[lesson]User uses FastAPI + SQLAlchemy for backend (switched from Django) | mentioned: 2026-08

(Above: a natural compression — filler removed, meaning intact. If removing words would lose nuance, keep the long version instead.)

Triggers (when to run a lifecycle pass)

Run a lifecycle pass when ANY of these is true:

  1. New session + stale memories: any injected hot-tier memory has a time marker (| mentioned: / | last discussed:) older than the previous calendar month (month difference ≥ 2) — run at most ONE pass per session.
  2. Periodic: recent_chats shows roughly 5+ conversations since the last cleanup — run a pass.
  3. User asks: "清理记忆" / "整理记忆" / "clean up" / "delete old memories" / "精简记忆".
  4. Store is crowded: total memory count > 30 (see Memory Tiers).

Lifecycle Pass Procedure

  1. Scan the injected hot-tier list, plus memory_tool(action="list") / search results for cold-tier candidates.
  2. Score each one using the 3 dimensions above.
  3. Act per decision rules — but batch conservatively:
    • Max ~3 edits + ~2 deletes per pass, unless user asked for a full cleanup.
    • Deletion is permanent and irreversible. Before deleting, confirm the fact has zero residual value AND that it can't be recovered from conversation_search if ever needed again. Confirm with the user before deleting more than one memory in a pass. When in doubt, archive instead.
  4. Report briefly (optional, in character): "我整理了一下记忆~归档了 X 条过时的,精简了 Y 条,删掉了 Z 条~"

Interaction with Other Sections

  • Memory Tiers: a lifecycle pass naturally moves hot → cold (via archive action on 4-5 scores, or via status: completed for plans).
  • create: rate limit (~3/session) keeps new clutter low, so lifecycle passes stay small.
  • edit: compression uses edit — normally preserve old context per the edit rules, EXCEPT compression itself (which deliberately drops only redundant wording, see Compression Rules).
  • delete: last resort only — truly valueless memories (wrong, superseded with zero residual value, noise). Never delete for age alone; prefer archive. When in doubt, keep.

Examples

Recording preferences (with importance)

User: "My name is Alex and I'm a backend developer"
→ Memory check: no existing identity memory
→ Two atomic facts → two separate memories (one fact per memory):
  memory_tool(action="create", content="[identity][!]User's name is Alex")
  memory_tool(action="create", content="[identity]User works as a backend developer")
→ Confirm: "记住啦~Alex,后端开发!"

Correcting a memory

User: "Actually I ditched VSCode for Zed"
→ Memory check: found id=5 "[preference]Uses VSCode with vim keybindings, values fast startup"
→ Edit (don't create new) — preserve old context:
  memory_tool(action="edit", id=5, content="[preference]Switched from VSCode to Zed editor, still values fast startup")

Recalling past context

User: "What was that database we decided on?"
→ Step 1: Scan injected memories for "[decision]" + "database"
→ Found: "[decision][!]Chose PostgreSQL over MySQL for side project — better JSON support"
→ Respond: "PostgreSQL!上次你选它是因为 JSON 支持比 MySQL 好~"

Searching when memory fails

User: "Remember that article I mentioned about Rust async?"
→ Step 1: Scan injected memories — nothing about Rust article
→ Step 2a: conversation_search("Rust async article")
→ Step 2b: conversation_search("Rust tokio tutorial")
→ Found match in conversation from 2026-07
→ Respond: "找到了!7月你提过一篇 Tokio 的教程文章~"
→ Also consider: should this be a memory? If important, create one now
  (with | mentioned: 2026-08).

Error Handling

  • If memory_tool returns an error, note it but don't retry more than once.
  • If conversation_search returns empty, try 2 more keyword variations before giving up.
  • If uncertain whether to remember, err on the side of not remembering — user can always tell you to remember later.

Existing Memory Compatibility

When taking over from a previous memory system, existing memories may not follow this format. That's fine:

  • Don't retroactively reformat old memories unless editing them for other reasons.
  • New memories must follow the [type] format.
  • Over time, old memories will naturally get edited into the new format.

Tools Used

ToolRole
memory_toolPersistent memory CRUD — the source of truth
conversation_searchForensic search of past dialogue
recent_chatsQuick topic continuity check
get_time_infoCurrent date — required for freshness scoring in lifecycle passes

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.