chore: fix all 702 clippy warnings across codebase - auto-fix 475 via cargo clippy --fix - fix remaining 227 manually: uninlined_format_args, redundant_closure, match_same_arms, underscore_binding, format_push_string, items_after_statements, needless_pass_by_value, clone_on_copy, case_sensitive_extension, single_match/let-else, write_with_newline, and other clippy lints
97 lines
3.7 KiB
Rust
97 lines
3.7 KiB
Rust
//! Tool for saving a new memory entry to persistent project memory.
|
|
|
|
use serde_json::{json, Value};
|
|
use anyhow::{Result, anyhow};
|
|
use super::super::Tool;
|
|
use super::super::ToolCtx;
|
|
use crate::model::memory::Memory;
|
|
|
|
/// 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})"))
|
|
}
|
|
}
|