Files
zesdex/crates/zesdex-backend/src/tool/memory/remember.rs
T
asepharyana be0a9582bb refactor: migrate monolithic crate to Cargo Workspace with Clean Architecture
Transform the single binary crate into a 9-crate workspace monorepo:

- Root Cargo.toml as [workspace] manager with resolver = "2"
- zesdex-entities: Domain entity types (session, settings, store, message, etc.)
- zesdex-utils: Pure utility functions (error, logger, pagination, slug, clipboard)
- zesdex-dto: Data Transfer Objects for LLM provider API communication
- zesdex-ipc: Unix-socket IPC layer (client/server/framing/protocol)
- zesdex-iam: Identity & Access Management (Clean Architecture: domain/application/infrastructure)
- zesdex-cms: Content Management (Clean Architecture: domain/application/infrastructure)
- zesdex-middleware: HTTP middleware (Auth, CORS, Rate Limiting)
- zesdex-libs: Composition root (AppContext, DB init, JWT, Argon2)
- zesdex-backend: Main binary entry point + seed/migrate binaries
- DevOps: Dockerfile, docker-compose, Nix (flake/shell/default), CI/CD updates
- Remove dead root src/ and src-misc/ directories

All crate re-exports maintain backward compatibility with original
crate::model::*, crate::dto::*, crate::ipc::* module paths.
Feature crates enforce strict layer separation: domain -> application
-> infrastructure with generic trait-based dependency injection.
2026-07-17 09:08:41 +07:00

101 lines
3.7 KiB
Rust

//! Tool for saving a new memory entry to persistent project memory.
use super::super::Tool;
use super::super::ToolCtx;
use crate::model::memory::Memory;
use anyhow::{anyhow, Result};
use serde_json::{json, Value};
/// Tool that writes a new `Memory` entry (name/description/content/kind) to disk.
pub struct Remember;
impl Tool for Remember {
fn name(&self) -> &'static str {
"remember"
}
fn description(&self) -> &'static str {
"Save a piece of information to persistent project memory. Memory entries are written to disk and can be retrieved later via the recall() tool. Use this to record conventions, preferences, and important context."
}
fn parameters(&self) -> Value {
json!({
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Short unique name for the memory (kebab-case, e.g. 'testing-conventions')"
},
"description": {
"type": "string",
"description": "One-line summary shown in the memory index"
},
"content": {
"type": "string",
"description": "The memory content body"
},
"kind": {
"type": "string",
"description": "Type of memory: 'project', 'reference', 'lesson', or 'feedback'",
"enum": ["project", "reference", "lesson", "feedback"]
}
},
"required": ["name", "description", "content", "kind"]
})
}
/// Build a `Memory` from the given args and persist it to `ctx.memory_dir`.
///
/// Flow: extract name/description/content/kind → validate name via `Memory::slugify`
/// → construct `Memory` with `lifecycle: "new"` and current timestamps →
/// `memory.write`.
///
/// Why: name must slugify to a valid filename (alphanumeric + hyphens, 1-80 chars)
/// since it's used directly as the on-disk file identifier.
///
/// Return: confirmation string on success; error if name is invalid or the write fails.
fn run(&self, ctx: &ToolCtx, args: &Value) -> Result<String> {
let name = args
.get("name")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("missing required argument: name"))?;
let description = args
.get("description")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("missing required argument: description"))?;
let content = args
.get("content")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("missing required argument: content"))?;
let kind = args
.get("kind")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("missing required argument: kind"))?;
if Memory::slugify(name).is_none() {
anyhow::bail!("invalid memory name: must produce a valid slug (alphanumeric + hyphens, 1-80 chars)");
}
let now = chrono::Utc::now().timestamp_millis();
let memory = Memory {
name: name.to_string(),
description: description.to_string(),
content: content.to_string(),
kind: kind.to_string(),
created_at: now,
updated_at: now,
outcome: None,
lifecycle: "new".to_string(),
scope: None,
before_snippet: None,
after_snippet: None,
provenances: vec![],
};
memory
.write(&ctx.memory_dir)
.map_err(|e| anyhow!("failed to write memory '{name}': {e}"))?;
Ok(format!("saved memory '{name}' ({kind})"))
}
}