From b458d07e373de97a4cf320ba32eeca4ecfc88d0f Mon Sep 17 00:00:00 2001 From: Mike Thicke Date: Mon, 25 May 2026 20:11:13 -0400 Subject: [PATCH] get_active_note tool Returns the path + content of the currently focused note, or null fields when no note is active. Read-only; mobile-safe. Closes #48. Co-Authored-By: Claude Opus 4.7 --- .../vault/__tests__/get_active_note.test.ts | 29 +++++++++++++++ src/agent/tools/vault/get_active_note.ts | 35 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 src/agent/tools/vault/__tests__/get_active_note.test.ts create mode 100644 src/agent/tools/vault/get_active_note.ts diff --git a/src/agent/tools/vault/__tests__/get_active_note.test.ts b/src/agent/tools/vault/__tests__/get_active_note.test.ts new file mode 100644 index 0000000..08998c4 --- /dev/null +++ b/src/agent/tools/vault/__tests__/get_active_note.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it, vi } from "vitest"; +import { App, TFile } from "obsidian"; + +import { getActiveNoteTool } from "@/agent/tools/vault/get_active_note"; +import type CoIntelligencePlugin from "@/CoIntelligencePlugin"; +import type { ToolExecutionContext } from "@/agent/types"; + +function makeCtx(app: App): ToolExecutionContext { + return { app, plugin: {} as CoIntelligencePlugin, toolCallId: "c" }; +} + +describe("getActiveNoteTool", () => { + it("returns nulls when no active file", async () => { + const app = new App(); + app.workspace.getActiveFile = vi.fn().mockReturnValue(null); + const out = await getActiveNoteTool.execute({}, makeCtx(app)); + expect(out).toEqual({ path: null, content: null }); + }); + + it("returns path + content for an active file", async () => { + const file = new TFile(); + file.path = "Active.md"; + const app = new App(); + app.workspace.getActiveFile = vi.fn().mockReturnValue(file); + app.vault.cachedRead = vi.fn().mockResolvedValue("body"); + const out = await getActiveNoteTool.execute({}, makeCtx(app)); + expect(out).toEqual({ path: "Active.md", content: "body" }); + }); +}); diff --git a/src/agent/tools/vault/get_active_note.ts b/src/agent/tools/vault/get_active_note.ts new file mode 100644 index 0000000..4ecf935 --- /dev/null +++ b/src/agent/tools/vault/get_active_note.ts @@ -0,0 +1,35 @@ +import { z } from "zod"; + +import type { CoiTool } from "@/agent/types"; + +const inputSchema = z.object({}); + +type Input = z.infer; + +interface Output { + path: string | null; + content: string | null; +} + +/** + * `get_active_note` — returns the path and content of the note the user has + * focused in Obsidian. Returns nulls when no note is active (e.g. a non-file + * pane is focused). Read-only; no approval. + */ +export const getActiveNoteTool: CoiTool = { + name: "get_active_note", + description: + "Get the path and content of the note the user is currently viewing in Obsidian, or nulls if no note is active.", + inputSchema, + requiresApproval: false, + scope: "vault", + platforms: ["desktop", "mobile"], + async execute(_input, { app }) { + const file = app.workspace.getActiveFile(); + if (!file) { + return { path: null, content: null }; + } + const content = await app.vault.cachedRead(file); + return { path: file.path, content }; + }, +};