Commit 6a87c7f0be2a

Vincent Demeester <vincent@sbr.pm>
2026-02-05 22:21:12
feat(session-history): added save_research, save_plan, and save_learning tools
Added three new tools to automatically save content to XDG-compliant directories: - save_research: Saves to ~/.local/share/ai/research/YYYY-MM/ with date prefix - save_plan: Saves to ~/.local/share/ai/plans/ (no date prefix, timeless) - save_learning: Saves to ~/.local/share/ai/learnings/YYYY-MM/ with date prefix These tools enable the AI to automatically organize different types of content into appropriate directories based on their nature (research, plans, learnings).
1 parent f29f9fd
Changed files (1)
dots
pi
agent
extensions
session-history
dots/pi/agent/extensions/session-history/index.ts
@@ -29,6 +29,9 @@ export default function (pi: ExtensionAPI) {
 	// Unified AI agent storage (XDG_DATA_HOME/ai)
 	const AI_DATA_DIR = join(homedir(), ".local", "share", "ai");
 	const SESSIONS_DIR = join(AI_DATA_DIR, "sessions");
+	const RESEARCH_DIR = join(AI_DATA_DIR, "research");
+	const PLANS_DIR = join(AI_DATA_DIR, "plans");
+	const LEARNINGS_DIR = join(AI_DATA_DIR, "learnings");
 
 	// Track current session file across multiple saves
 	let currentSessionFile: string | null = null;
@@ -232,6 +235,193 @@ export default function (pi: ExtensionAPI) {
 		},
 	});
 
+	// Tool to save research documents
+	pi.registerTool({
+		name: "save_research",
+		label: "Save Research",
+		description:
+			"Saves research findings to ~/.local/share/ai/research/YYYY-MM/. Use for exploratory research, technical investigation, or background study on a topic. Research is date-organized for temporal context.",
+		parameters: {
+			type: "object",
+			properties: {
+				title: {
+					type: "string",
+					description: "Brief title for the research (e.g., 'llm-cost-tracking')",
+				},
+				content: {
+					type: "string",
+					description: "Full markdown content of the research document",
+				},
+			},
+			required: ["title", "content"],
+		},
+		async execute(toolCallId, params, signal, onUpdate, ctx) {
+			try {
+				const { title, content } = params;
+				const { yearMonth, date } = getDateInfo();
+				const researchDir = join(RESEARCH_DIR, yearMonth);
+
+				// Sanitize filename
+				const slug = title
+					.toLowerCase()
+					.replace(/[^a-z0-9]+/g, "-")
+					.replace(/^-+|-+$/g, "")
+					.substring(0, 60);
+
+				const filename = `${date}-${slug}.md`;
+				const filepath = join(researchDir, filename);
+
+				await mkdir(researchDir, { recursive: true });
+				await writeFile(filepath, content, "utf-8");
+				await appendToSessionLog(`Research saved: ${filename}`);
+
+				return {
+					content: [
+						{
+							type: "text",
+							text: `โœ“ Research saved to: ${filepath}`,
+						},
+					],
+					details: { created: true },
+				};
+			} catch (error) {
+				return {
+					content: [
+						{
+							type: "text",
+							text: `Error saving research: ${error}`,
+						},
+					],
+					details: { error: String(error) },
+				};
+			}
+		},
+	});
+
+	// Tool to save plans
+	pi.registerTool({
+		name: "save_plan",
+		label: "Save Plan",
+		description:
+			"Saves a plan to ~/.local/share/ai/plans/. Use for project plans, roadmaps, or structured action plans. Plans are NOT date-organized as they represent timeless strategies.",
+		parameters: {
+			type: "object",
+			properties: {
+				title: {
+					type: "string",
+					description: "Brief title for the plan (e.g., 'homelab-migration-plan')",
+				},
+				content: {
+					type: "string",
+					description: "Full markdown content of the plan",
+				},
+			},
+			required: ["title", "content"],
+		},
+		async execute(toolCallId, params, signal, onUpdate, ctx) {
+			try {
+				const { title, content } = params;
+
+				// Sanitize filename (no date prefix for plans)
+				const slug = title
+					.toLowerCase()
+					.replace(/[^a-z0-9]+/g, "-")
+					.replace(/^-+|-+$/g, "")
+					.substring(0, 60);
+
+				const filename = `${slug}.md`;
+				const filepath = join(PLANS_DIR, filename);
+
+				await mkdir(PLANS_DIR, { recursive: true });
+				await writeFile(filepath, content, "utf-8");
+				await appendToSessionLog(`Plan saved: ${filename}`);
+
+				return {
+					content: [
+						{
+							type: "text",
+							text: `โœ“ Plan saved to: ${filepath}`,
+						},
+					],
+					details: { created: true },
+				};
+			} catch (error) {
+				return {
+					content: [
+						{
+							type: "text",
+							text: `Error saving plan: ${error}`,
+						},
+					],
+					details: { error: String(error) },
+				};
+			}
+		},
+	});
+
+	// Tool to save learnings
+	pi.registerTool({
+		name: "save_learning",
+		label: "Save Learning",
+		description:
+			"Saves a learning or insight to ~/.local/share/ai/learnings/YYYY-MM/. Use for lessons learned, insights gained, or knowledge discoveries. Learnings are date-organized to track knowledge evolution.",
+		parameters: {
+			type: "object",
+			properties: {
+				title: {
+					type: "string",
+					description: "Brief title for the learning (e.g., 'nix-flake-patterns')",
+				},
+				content: {
+					type: "string",
+					description: "Full markdown content describing the learning or insight",
+				},
+			},
+			required: ["title", "content"],
+		},
+		async execute(toolCallId, params, signal, onUpdate, ctx) {
+			try {
+				const { title, content } = params;
+				const { yearMonth, date } = getDateInfo();
+				const learningsDir = join(LEARNINGS_DIR, yearMonth);
+
+				// Sanitize filename
+				const slug = title
+					.toLowerCase()
+					.replace(/[^a-z0-9]+/g, "-")
+					.replace(/^-+|-+$/g, "")
+					.substring(0, 60);
+
+				const filename = `${date}-${slug}.md`;
+				const filepath = join(learningsDir, filename);
+
+				await mkdir(learningsDir, { recursive: true });
+				await writeFile(filepath, content, "utf-8");
+				await appendToSessionLog(`Learning saved: ${filename}`);
+
+				return {
+					content: [
+						{
+							type: "text",
+							text: `โœ“ Learning saved to: ${filepath}`,
+						},
+					],
+					details: { created: true },
+				};
+			} catch (error) {
+				return {
+					content: [
+						{
+							type: "text",
+							text: `Error saving learning: ${error}`,
+						},
+					],
+					details: { error: String(error) },
+				};
+			}
+		},
+	});
+
 	// Log session start and reset session tracking
 	pi.on("session_start", async (_event, ctx) => {
 		try {