Commit 6a87c7f0be2a
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 {