- README.MD: add original-source-code and nano-claude-code sections, update overview table (4 subprojects), add v3.0 news entry, expand comparison table with memory/multi-agent/skills dimensions - nano-claude-code v3.0: multi-agent package (multi_agent/), memory package (memory/), skill package (skill/) with built-in /commit and /review skills, context compression (compaction.py), tool registry plugin system, diff view, 17 slash commands, 18 built-in tools, 101 tests (~5000 lines total) - original-source-code/src: add raw TypeScript source tree (1884 files) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
217 lines
7.7 KiB
Python
217 lines
7.7 KiB
Python
"""Memory tool registrations: MemorySave, MemoryDelete, MemorySearch.
|
|
|
|
Importing this module registers the three tools into the central registry.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from tool_registry import ToolDef, register_tool
|
|
from .store import MemoryEntry, save_memory, delete_memory, load_index
|
|
from .context import find_relevant_memories
|
|
from .scan import scan_all_memories, format_memory_manifest
|
|
|
|
|
|
# ── Tool implementations ───────────────────────────────────────────────────
|
|
|
|
def _memory_save(params: dict, config: dict) -> str:
|
|
"""Save or update a persistent memory entry."""
|
|
entry = MemoryEntry(
|
|
name=params["name"],
|
|
description=params["description"],
|
|
type=params["type"],
|
|
content=params["content"],
|
|
created=datetime.now().strftime("%Y-%m-%d"),
|
|
)
|
|
scope = params.get("scope", "user")
|
|
save_memory(entry, scope=scope)
|
|
|
|
scope_label = "project" if scope == "project" else "user"
|
|
return f"Memory saved: '{entry.name}' [{entry.type}/{scope_label}]"
|
|
|
|
|
|
def _memory_delete(params: dict, config: dict) -> str:
|
|
"""Delete a persistent memory entry by name."""
|
|
name = params["name"]
|
|
scope = params.get("scope", "user")
|
|
delete_memory(name, scope=scope)
|
|
return f"Memory deleted: '{name}' (scope: {scope})"
|
|
|
|
|
|
def _memory_search(params: dict, config: dict) -> str:
|
|
"""Search memories by keyword query with optional AI relevance filtering."""
|
|
query = params["query"]
|
|
use_ai = params.get("use_ai", False)
|
|
max_results = params.get("max_results", 5)
|
|
|
|
results = find_relevant_memories(
|
|
query, max_results=max_results, use_ai=use_ai, config=config
|
|
)
|
|
|
|
if not results:
|
|
return f"No memories found matching '{query}'."
|
|
|
|
lines = [f"Found {len(results)} relevant memory/memories for '{query}':", ""]
|
|
for r in results:
|
|
freshness = f" ⚠ {r['freshness_text']}" if r["freshness_text"] else ""
|
|
lines.append(
|
|
f"[{r['type']}/{r['scope']}] {r['name']}\n"
|
|
f" {r['description']}\n"
|
|
f" {r['content'][:200]}{'...' if len(r['content']) > 200 else ''}"
|
|
f"{freshness}"
|
|
)
|
|
return "\n\n".join(lines)
|
|
|
|
|
|
def _memory_list(params: dict, config: dict) -> str:
|
|
"""List all memory entries with their manifest (type, scope, age, description)."""
|
|
headers = scan_all_memories()
|
|
if not headers:
|
|
return "No memories stored."
|
|
|
|
scope_filter = params.get("scope", "all")
|
|
if scope_filter != "all":
|
|
headers = [h for h in headers if h.scope == scope_filter]
|
|
if not headers:
|
|
return f"No {scope_filter} memories stored."
|
|
|
|
manifest = format_memory_manifest(headers)
|
|
return f"{len(headers)} memory/memories:\n\n{manifest}"
|
|
|
|
|
|
# ── Tool registrations ─────────────────────────────────────────────────────
|
|
|
|
register_tool(ToolDef(
|
|
name="MemorySave",
|
|
schema={
|
|
"name": "MemorySave",
|
|
"description": (
|
|
"Save a persistent memory entry as a markdown file with frontmatter. "
|
|
"Use for information that should persist across conversations: "
|
|
"user preferences, feedback/corrections, project context, or external references. "
|
|
"Do NOT save: code patterns, architecture, git history, or task state.\n\n"
|
|
"For feedback/project memories, structure content as: "
|
|
"rule/fact, then **Why:** and **How to apply:** lines."
|
|
),
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"name": {
|
|
"type": "string",
|
|
"description": "Human-readable name (becomes the filename slug)",
|
|
},
|
|
"type": {
|
|
"type": "string",
|
|
"enum": ["user", "feedback", "project", "reference"],
|
|
"description": (
|
|
"user=preferences/role, feedback=guidance on how to work, "
|
|
"project=ongoing work/decisions, reference=external system pointers"
|
|
),
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"description": "Short one-line description (used for relevance decisions — be specific)",
|
|
},
|
|
"content": {
|
|
"type": "string",
|
|
"description": "Body text. For feedback/project: rule/fact + **Why:** + **How to apply:**",
|
|
},
|
|
"scope": {
|
|
"type": "string",
|
|
"enum": ["user", "project"],
|
|
"description": (
|
|
"'user' (default) = ~/.nano_claude/memory/ shared across projects; "
|
|
"'project' = .nano_claude/memory/ local to this project"
|
|
),
|
|
},
|
|
},
|
|
"required": ["name", "type", "description", "content"],
|
|
},
|
|
},
|
|
func=_memory_save,
|
|
read_only=False,
|
|
concurrent_safe=False,
|
|
))
|
|
|
|
register_tool(ToolDef(
|
|
name="MemoryDelete",
|
|
schema={
|
|
"name": "MemoryDelete",
|
|
"description": "Delete a persistent memory entry by name.",
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"name": {"type": "string", "description": "Name of the memory to delete"},
|
|
"scope": {
|
|
"type": "string",
|
|
"enum": ["user", "project"],
|
|
"description": "Scope to delete from (default: 'user')",
|
|
},
|
|
},
|
|
"required": ["name"],
|
|
},
|
|
},
|
|
func=_memory_delete,
|
|
read_only=False,
|
|
concurrent_safe=False,
|
|
))
|
|
|
|
register_tool(ToolDef(
|
|
name="MemorySearch",
|
|
schema={
|
|
"name": "MemorySearch",
|
|
"description": (
|
|
"Search persistent memories by keyword. Returns matching entries with "
|
|
"content preview and staleness warning for old memories. "
|
|
"Set use_ai=true to use AI-powered relevance ranking (costs a small API call)."
|
|
),
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"query": {"type": "string", "description": "Search query"},
|
|
"max_results": {
|
|
"type": "integer",
|
|
"description": "Maximum results to return (default: 5)",
|
|
},
|
|
"use_ai": {
|
|
"type": "boolean",
|
|
"description": "Use AI relevance ranking (default: false = keyword only)",
|
|
},
|
|
"scope": {
|
|
"type": "string",
|
|
"enum": ["user", "project", "all"],
|
|
"description": "Which scope to search (default: 'all')",
|
|
},
|
|
},
|
|
"required": ["query"],
|
|
},
|
|
},
|
|
func=_memory_search,
|
|
read_only=True,
|
|
concurrent_safe=True,
|
|
))
|
|
|
|
register_tool(ToolDef(
|
|
name="MemoryList",
|
|
schema={
|
|
"name": "MemoryList",
|
|
"description": (
|
|
"List all memory entries with type, scope, age, and description. "
|
|
"Useful for reviewing what's been remembered before deciding to save or delete."
|
|
),
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"scope": {
|
|
"type": "string",
|
|
"enum": ["user", "project", "all"],
|
|
"description": "Which scope to list (default: 'all')",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
func=_memory_list,
|
|
read_only=True,
|
|
concurrent_safe=True,
|
|
))
|