/** * KHAELOR * File: src/tools/remember.ts * Description: The remember tool — persist a durable project fact into .khaelor/MEMORY.md with provenance (v2 design §5). * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ import * as path from "node:path"; import type { ToolDefinition } from "./registry.js"; import type { ToolContext, ToolResult } from "./types.js"; export interface RememberInput { fact: string; section: string; confidence?: string; } const DESCRIPTION = "Persist a durable fact about this project into its memory (.khaelor/MEMORY.md) — a convention, a " + "build command, a pitfall, an architectural constraint. Use it when you discover something future " + "sessions must know. Do NOT store transient task state. section: conventions | commands | pitfalls " + "| architecture | notes. confidence defaults to medium."; const MEMORY_RELATIVE = ".khaelor/MEMORY.md"; // Local re-implementation seam: the memory module's append logic is imported // lazily to keep the tools layer free of static cross-module dependencies. async function appendToMemory( existing: string | null, entry: { section: string; text: string; provenance: { session: string; tool: string; confidence: "high" | "medium" | "low"; date: string }; }, ): Promise { const { appendMemoryEntry } = await import("../memory/store.js"); return appendMemoryEntry(existing, entry); } async function executeRemember(input: RememberInput, ctx: ToolContext): Promise { const startedAt = Date.now(); const fact = input.fact.trim(); if (fact.length === 0) { return { content: 'Parameter "fact" must be a non-empty durable fact about the project.', isError: true, metadata: { title: "remember · empty fact", durationMs: Date.now() - startedAt }, }; } const confidence = input.confidence === "high" || input.confidence === "low" ? input.confidence : "medium"; const filePath = path.join(ctx.workspace.cwd(), MEMORY_RELATIVE); let existing: string | null = null; try { existing = await ctx.workspace.readFile(filePath); } catch { existing = null; // first memory — the file is created below } const updated = await appendToMemory(existing, { section: input.section, text: fact, provenance: { session: ctx.sessionId, tool: ctx.callId, confidence, date: new Date().toISOString().slice(0, 10), }, }); await ctx.workspace.writeFile(filePath, updated); ctx.emit({ type: "memory.written", payload: { section: input.section, entry: fact, confidence, toolUseId: ctx.callId }, }); return { content: `Remembered under "${input.section}" (confidence: ${confidence}).`, metadata: { title: `Remember · ${input.section}`, durationMs: Date.now() - startedAt, extra: { confidence }, }, }; } /** Build the remember tool (project memory, v2 §5). */ export function createRememberTool(): ToolDefinition { return { name: "remember", description: DESCRIPTION, inputSchema: { type: "object", properties: { fact: { type: "string", description: "The durable fact, one or two sentences." }, section: { type: "string", description: "Where it belongs: conventions, commands, pitfalls, architecture, or notes.", enum: ["conventions", "commands", "pitfalls", "architecture", "notes"], }, confidence: { type: "string", description: "How certain you are: high, medium (default), or low.", enum: ["high", "medium", "low"], }, }, required: ["fact", "section"], }, capability: "file.write", execute: executeRemember, }; }