diff --git a/eslint.config.mjs b/eslint.config.mjs index 80e1a8f9..910c1be3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -95,8 +95,6 @@ export default [ // --- Medium: promise / method ergonomics --- "@typescript-eslint/unbound-method": "off", // 68 violations - "@typescript-eslint/no-misused-promises": "off", // 46 violations - "@typescript-eslint/no-unnecessary-type-assertion": "off", // 40 violations "@typescript-eslint/no-floating-promises": "off", // 39 violations // --- Quick wins: small enough to fix and enable in a single PR --- @@ -172,6 +170,14 @@ export default [ "@typescript-eslint/no-empty-function": "off", "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/no-unused-vars": ["error", { args: "none" }], + // checksVoidReturn relaxed for: + // - attributes: async event handlers in JSX (onClick={async () => ...}) are + // the standard React pattern; React already handles them correctly. + // - inheritedMethods: Obsidian's Plugin.onload/onunload are commonly async. + "@typescript-eslint/no-misused-promises": [ + "error", + { checksVoidReturn: { attributes: false, inheritedMethods: false } }, + ], // TypeScript handles undefined-identifier detection (and does so cross-realm // correctly); per typescript-eslint's own guidance, disable no-undef on TS. "no-undef": "off", diff --git a/src/LLMProviders/ChatOpenRouter.ts b/src/LLMProviders/ChatOpenRouter.ts index 8f7484fc..8b2ce773 100644 --- a/src/LLMProviders/ChatOpenRouter.ts +++ b/src/LLMProviders/ChatOpenRouter.ts @@ -243,7 +243,7 @@ export class ChatOpenRouter extends ChatOpenAI { role: "tool", content: msg.content, tool_call_id: msg.tool_call_id, - } as OpenRouterMessageParam; + }; } if (msg.additional_kwargs?.function_call) { diff --git a/src/LLMProviders/githubCopilot/GitHubCopilotChatModel.ts b/src/LLMProviders/githubCopilot/GitHubCopilotChatModel.ts index 1b4fd4d1..561f7f88 100644 --- a/src/LLMProviders/githubCopilot/GitHubCopilotChatModel.ts +++ b/src/LLMProviders/githubCopilot/GitHubCopilotChatModel.ts @@ -101,7 +101,7 @@ export class GitHubCopilotChatModel extends ChatOpenAICompletions { const provider = GitHubCopilotProvider.getInstance(); // scorecard: streaming requires fetch — cannot use requestUrl - const baseFetch = fetchImplementation ?? (configuration?.fetch as FetchImplementation) ?? fetch; + const baseFetch = fetchImplementation ?? configuration?.fetch ?? fetch; const authedFetch = GitHubCopilotChatModel.buildAuthedFetch(provider, baseFetch); super({ @@ -115,7 +115,7 @@ export class GitHubCopilotChatModel extends ChatOpenAICompletions { configuration: { ...(configuration ?? {}), // Reason: OpenAI SDK appends "/chat/completions" to baseURL automatically - baseURL: (configuration?.baseURL as string) ?? COPILOT_API_BASE, + baseURL: configuration?.baseURL ?? COPILOT_API_BASE, fetch: authedFetch, }, }); diff --git a/src/LLMProviders/githubCopilot/GitHubCopilotResponsesModel.ts b/src/LLMProviders/githubCopilot/GitHubCopilotResponsesModel.ts index acf1c8e6..9044ff80 100644 --- a/src/LLMProviders/githubCopilot/GitHubCopilotResponsesModel.ts +++ b/src/LLMProviders/githubCopilot/GitHubCopilotResponsesModel.ts @@ -90,7 +90,7 @@ export class GitHubCopilotResponsesModel extends ChatOpenAI { const { fetchImplementation, configuration, apiKey, ...rest } = fields; const provider = GitHubCopilotProvider.getInstance(); - const baseFetch = fetchImplementation ?? (configuration?.fetch as FetchImplementation) ?? fetch; + const baseFetch = fetchImplementation ?? configuration?.fetch ?? fetch; const authedFetch = buildGitHubCopilotAuthedFetch(provider, baseFetch); super({ @@ -100,7 +100,7 @@ export class GitHubCopilotResponsesModel extends ChatOpenAI { streamUsage: false, configuration: { ...(configuration ?? {}), - baseURL: (configuration?.baseURL as string) ?? COPILOT_API_BASE, + baseURL: configuration?.baseURL ?? COPILOT_API_BASE, fetch: authedFetch, }, }); diff --git a/src/LLMProviders/projectManager.ts b/src/LLMProviders/projectManager.ts index 737e45e9..9278ff9a 100644 --- a/src/LLMProviders/projectManager.ts +++ b/src/LLMProviders/projectManager.ts @@ -57,11 +57,11 @@ export default class ProjectManager { this.loadTracker = ProjectLoadTracker.getInstance(this.app); // Set up subscriptions - subscribeToModelKeyChange(async () => { - await this.getCurrentChainManager().createChainWithNewModel(); + subscribeToModelKeyChange(() => { + void this.getCurrentChainManager().createChainWithNewModel(); }); - subscribeToChainTypeChange(async () => { + subscribeToChainTypeChange(() => { // When switching from other modes to project mode, no need to update the chain. if (isProjectMode()) { return; @@ -72,14 +72,14 @@ export default class ProjectManager { settings.indexVaultToVectorStore === VAULT_VECTOR_STORE_STRATEGY.ON_MODE_SWITCH && (getChainType() === ChainType.VAULT_QA_CHAIN || getChainType() === ChainType.COPILOT_PLUS_CHAIN); - await this.getCurrentChainManager().createChainWithNewModel({ + void this.getCurrentChainManager().createChainWithNewModel({ refreshIndex: shouldAutoIndex, }); }); // Subscribe to Project changes - subscribeToProjectChange(async (project) => { - await this.switchProject(project); + subscribeToProjectChange((project) => { + void this.switchProject(project); }); // Subscribe to project cache changes to monitor project modifications diff --git a/src/commands/customCommandUtils.test.ts b/src/commands/customCommandUtils.test.ts index 3e1b3df7..8dc902bf 100644 --- a/src/commands/customCommandUtils.test.ts +++ b/src/commands/customCommandUtils.test.ts @@ -111,7 +111,7 @@ describe("processedPrompt()", () => { .mockResolvedValueOnce("here is the note content for note0") .mockResolvedValueOnce("note content for note1"); - const { getFileName } = jest.requireMock("@/utils") as any; + const { getFileName } = jest.requireMock("@/utils"); getFileName.mockReturnValueOnce("Variable1 Note").mockReturnValueOnce("Variable2 Note"); const mockNote1 = mockTFile({ path: "path/to/note1.md", basename: "Variable1 Note" }); @@ -175,7 +175,7 @@ describe("processedPrompt()", () => { const selectedText = ""; (getFileContent as jest.Mock).mockResolvedValue("Content of the active note"); - const { getFileName } = jest.requireMock("@/utils") as any; + const { getFileName } = jest.requireMock("@/utils"); getFileName.mockReturnValue("Active Note"); const result = await processPrompt(doc.content, selectedText, mockVault, mockActiveNote); @@ -238,7 +238,7 @@ describe("processedPrompt()", () => { (getNotesFromTags as jest.Mock).mockReturnValue([mockNoteForTag]); // Mock getFileName to return the basename - const { getFileName } = jest.requireMock("@/utils") as any; + const { getFileName } = jest.requireMock("@/utils"); getFileName.mockReturnValue("Tagged Note"); // Mock getFileContent to return content for the note @@ -270,7 +270,7 @@ describe("processedPrompt()", () => { (getNotesFromTags as jest.Mock).mockReturnValue([mockNoteForTag1, mockNoteForTag2]); // Mock getFileName to return the basename - const { getFileName } = jest.requireMock("@/utils") as any; + const { getFileName } = jest.requireMock("@/utils"); getFileName.mockImplementation((file: TFile) => file.basename); // Mock getFileContent to return content for each note @@ -324,7 +324,7 @@ describe("processedPrompt()", () => { (extractTemplateNoteFiles as jest.Mock).mockReturnValue([mockNoteFile]); - const { getFileName } = jest.requireMock("@/utils") as any; + const { getFileName } = jest.requireMock("@/utils"); getFileName.mockReturnValue("Test Note"); (getFileContent as jest.Mock).mockResolvedValue("Test note content"); @@ -362,7 +362,7 @@ describe("processedPrompt()", () => { // Only Note1 should be extracted since it's wrapped in {[[]]} (extractTemplateNoteFiles as jest.Mock).mockReturnValue([mockNote1]); - const { getFileName } = jest.requireMock("@/utils") as any; + const { getFileName } = jest.requireMock("@/utils"); getFileName.mockImplementation((file: TFile) => file.basename); (getFileContent as jest.Mock).mockImplementation((file: TFile) => { @@ -459,7 +459,7 @@ describe("processedPrompt()", () => { const selectedText = ""; // Mock getFileName and getFileContent - const { getFileName } = jest.requireMock("@/utils") as any; + const { getFileName } = jest.requireMock("@/utils"); getFileName.mockReturnValue("Active Note"); (getFileContent as jest.Mock).mockResolvedValue("Content of the active note"); @@ -511,7 +511,7 @@ describe("processedPrompt()", () => { // Mock getFileContent for the active note when processed via {} (getFileContent as jest.Mock).mockResolvedValue("Content of the active note"); - const { getFileName } = jest.requireMock("@/utils") as any; + const { getFileName } = jest.requireMock("@/utils"); getFileName.mockReturnValue("Active Note"); const result = await processPrompt(doc.content, selectedText, mockVault, mockActiveNote); diff --git a/src/components/chat-components/AtMentionTypeahead.tsx b/src/components/chat-components/AtMentionTypeahead.tsx index 448c1677..b8bc5567 100644 --- a/src/components/chat-components/AtMentionTypeahead.tsx +++ b/src/components/chat-components/AtMentionTypeahead.tsx @@ -183,7 +183,7 @@ export function AtMentionTypeahead({ return ( { }; }); -const { __renderMarkdownMock: renderMarkdownMock } = jest.requireMock("obsidian") as { - __renderMarkdownMock: jest.Mock; -}; +const { __renderMarkdownMock: renderMarkdownMock } = jest.requireMock("obsidian"); // --------------------------------------------------------------------------- // Verifies that the HTML string passed to MarkdownRenderer.renderMarkdown diff --git a/src/components/chat-components/hooks/useAtMentionSearch.ts b/src/components/chat-components/hooks/useAtMentionSearch.ts index 93ca2c2e..e41c45c4 100644 --- a/src/components/chat-components/hooks/useAtMentionSearch.ts +++ b/src/components/chat-components/hooks/useAtMentionSearch.ts @@ -48,7 +48,7 @@ export function useAtMentionSearch( key: `note-${file.basename}-${index}`, title: file.basename, subtitle: file.path, - category: "notes" as AtMentionCategory, + category: "notes", data: file, content: undefined, icon: React.createElement(FileText, { className: "tw-size-4" }), @@ -64,7 +64,7 @@ export function useAtMentionSearch( key: `tool-${tool}`, title: tool, subtitle: getToolDescription(tool), - category: "tools" as AtMentionCategory, + category: "tools", data: tool, content: getToolDescription(tool), icon: React.createElement(Wrench, { className: "tw-size-4" }), @@ -79,7 +79,7 @@ export function useAtMentionSearch( key: `folder-${folder.path}`, title: folder.name, subtitle: folder.path, - category: "folders" as AtMentionCategory, + category: "folders", data: folder, content: undefined, icon: React.createElement(Folder, { className: "tw-size-4" }), @@ -97,7 +97,7 @@ export function useAtMentionSearch( key: `webtab-${tab.url || tab.title || index}-${index}`, title: tab.title || "Untitled", subtitle: isLoaded ? tab.url : "Tab not loaded", - category: "webTabs" as AtMentionCategory, + category: "webTabs", data: tab, content: undefined, disabled: !isLoaded, @@ -129,7 +129,7 @@ export function useAtMentionSearch( key: "active-web-tab", title: "Active Web Tab", subtitle: undefined, - category: "activeWebTab" as AtMentionCategory, + category: "activeWebTab", data: activeWebTab, content: undefined, icon: React.createElement(Globe, { className: "tw-size-4" }), @@ -142,7 +142,7 @@ export function useAtMentionSearch( key: `active-note-${currentActiveFile.path}`, title: "Active Note", subtitle: undefined, - category: "activeNote" as AtMentionCategory, + category: "activeNote", data: currentActiveFile, content: undefined, icon: React.createElement(FileClock, { className: "tw-size-4" }), diff --git a/src/components/chat-components/plugins/AtMentionCommandPlugin.tsx b/src/components/chat-components/plugins/AtMentionCommandPlugin.tsx index 95a9f481..b9529daf 100644 --- a/src/components/chat-components/plugins/AtMentionCommandPlugin.tsx +++ b/src/components/chat-components/plugins/AtMentionCommandPlugin.tsx @@ -186,7 +186,7 @@ export function AtMentionCommandPlugin({ <> {state.isOpen && ( void) { + constructor(app: App, onConfirm: () => void | Promise) { super( app, onConfirm, diff --git a/src/components/modals/ResetSettingsConfirmModal.tsx b/src/components/modals/ResetSettingsConfirmModal.tsx index bfa192bc..32c065c6 100644 --- a/src/components/modals/ResetSettingsConfirmModal.tsx +++ b/src/components/modals/ResetSettingsConfirmModal.tsx @@ -2,7 +2,7 @@ import { ConfirmModal } from "@/components/modals/ConfirmModal"; import { App } from "obsidian"; export class ResetSettingsConfirmModal extends ConfirmModal { - constructor(app: App, onConfirm: () => void) { + constructor(app: App, onConfirm: () => void | Promise) { super( app, onConfirm, diff --git a/src/components/modals/project/AddProjectModal.tsx b/src/components/modals/project/AddProjectModal.tsx index c12ab8bd..a7aa6dc5 100644 --- a/src/components/modals/project/AddProjectModal.tsx +++ b/src/components/modals/project/AddProjectModal.tsx @@ -218,7 +218,7 @@ function AddProjectModalContent({ try { setIsSubmitting(true); - await onSave(saveData as ProjectConfig); + await onSave(saveData); } catch (e) { new Notice(err2String(e)); setTouched((prev) => ({ diff --git a/src/components/quick-ask/QuickAskOverlay.tsx b/src/components/quick-ask/QuickAskOverlay.tsx index 1b9a5fe6..05e5d516 100644 --- a/src/components/quick-ask/QuickAskOverlay.tsx +++ b/src/components/quick-ask/QuickAskOverlay.tsx @@ -435,7 +435,7 @@ export class QuickAskOverlay { const getVisibleRect = (pos: number | null): AnchorRect | null => { if (typeof pos !== "number") return null; - const coords = this.options.view.coordsAtPos(pos) as AnchorRect | null; + const coords = this.options.view.coordsAtPos(pos); if (!coords) return null; return this.isAnchorRectVisible(coords, visibleRect) ? coords : null; }; diff --git a/src/components/ui/mobile-card.tsx b/src/components/ui/mobile-card.tsx index 4c9f7923..37bf26d2 100644 --- a/src/components/ui/mobile-card.tsx +++ b/src/components/ui/mobile-card.tsx @@ -15,7 +15,7 @@ import { ChevronDown, ChevronRight, GripVertical, MoreVertical } from "lucide-re export interface MobileCardDropdownAction { icon: React.ReactNode; label: string; - onClick: (item: T) => void; + onClick: (item: T) => void | Promise; variant?: "default" | "destructive"; } diff --git a/src/encryptionService.test.ts b/src/encryptionService.test.ts index cb455075..490285c5 100644 --- a/src/encryptionService.test.ts +++ b/src/encryptionService.test.ts @@ -15,7 +15,7 @@ const mockElectron = { jest.mock("electron", () => mockElectron); -window.TextEncoder = TextEncoder as any; +window.TextEncoder = TextEncoder; window.TextDecoder = TextDecoder as any; // Now we can import our modules diff --git a/src/hooks/useChatFileDrop.ts b/src/hooks/useChatFileDrop.ts index 6094c9ed..c16a8dc9 100644 --- a/src/hooks/useChatFileDrop.ts +++ b/src/hooks/useChatFileDrop.ts @@ -241,16 +241,20 @@ export function useChatFileDrop(props: UseChatFileDropProps): UseChatFileDropRet } }; + const handleDropEvent = (e: DragEvent) => { + void handleDrop(e); + }; + // Attach event listeners container.addEventListener("dragover", handleDragOver); container.addEventListener("dragleave", handleDragLeave); - container.addEventListener("drop", handleDrop); + container.addEventListener("drop", handleDropEvent); // Cleanup return () => { container.removeEventListener("dragover", handleDragOver); container.removeEventListener("dragleave", handleDragLeave); - container.removeEventListener("drop", handleDrop); + container.removeEventListener("drop", handleDropEvent); }; }, [app, contextNotes, selectedImages, onAddImage, setContextNotes, containerRef]); diff --git a/src/main.ts b/src/main.ts index b3ae6a1b..67e49e72 100644 --- a/src/main.ts +++ b/src/main.ts @@ -103,13 +103,15 @@ export default class CopilotPlugin extends Plugin { async onload(): Promise { await this.loadSettings(); - this.settingsUnsubscriber = subscribeToSettingsChange(async (prev, next) => { - if (next.enableEncryption) { - await this.saveData(await encryptAllKeys(next)); - } else { - await this.saveData(next); - } - registerCommands(this, prev, next); + this.settingsUnsubscriber = subscribeToSettingsChange((prev, next) => { + void (async () => { + if (next.enableEncryption) { + await this.saveData(await encryptAllKeys(next)); + } else { + await this.saveData(next); + } + registerCommands(this, prev, next); + })(); }); this.addSettingTab(new CopilotSettingTab(this.app, this)); diff --git a/src/miyo/MiyoServiceDiscovery.test.ts b/src/miyo/MiyoServiceDiscovery.test.ts index 0c417f81..217e8960 100644 --- a/src/miyo/MiyoServiceDiscovery.test.ts +++ b/src/miyo/MiyoServiceDiscovery.test.ts @@ -249,7 +249,7 @@ describe("MiyoServiceDiscovery", () => { host: "127.0.0.1", port: 8742, pid: 999, - } as MiyoServiceConfig), + }), }, } ); diff --git a/src/search/v3/SearchCore.test.ts b/src/search/v3/SearchCore.test.ts index 302ed90f..63235d3a 100644 --- a/src/search/v3/SearchCore.test.ts +++ b/src/search/v3/SearchCore.test.ts @@ -114,7 +114,7 @@ describe("SearchCore retrieve", () => { }); it("should pass queries and salient terms as recall queries", async () => { - const searchCore = new SearchCore(mockApp as any); + const searchCore = new SearchCore(mockApp); const retrieveResult = await searchCore.retrieve("#ProjectAlpha/Phase1 update"); @@ -128,7 +128,7 @@ describe("SearchCore retrieve", () => { }); it("should bypass ceilings when returnAll is enabled", async () => { - const searchCore = new SearchCore(mockApp as any); + const searchCore = new SearchCore(mockApp); buildFromCandidatesMock.mockResolvedValueOnce(5); batchCachedReadGrepMock.mockResolvedValueOnce(["note1.md", "note2.md"]); diff --git a/src/search/v3/TieredLexicalRetriever.test.ts b/src/search/v3/TieredLexicalRetriever.test.ts index e045efc8..d524b9ad 100644 --- a/src/search/v3/TieredLexicalRetriever.test.ts +++ b/src/search/v3/TieredLexicalRetriever.test.ts @@ -90,7 +90,7 @@ describe("TieredLexicalRetriever", () => { if (path === "note1.md" || path === "note2.md") { const file = new (TFile as any)(path); Object.setPrototypeOf(file, (TFile as any).prototype); - (file as any).stat = { mtime: 1000, ctime: 1000 }; + file.stat = { mtime: 1000, ctime: 1000 }; return file; } return null; @@ -209,7 +209,7 @@ describe("TieredLexicalRetriever", () => { if (path === "test.md") { const file = new (TFile as any)(path); Object.setPrototypeOf(file, (TFile as any).prototype); - (file as any).stat = { mtime: 1000, ctime: 1000 }; + file.stat = { mtime: 1000, ctime: 1000 }; return file; } return null; @@ -260,7 +260,7 @@ describe("TieredLexicalRetriever", () => { if (path === "large.md" || path === "other.md") { const file = new (TFile as any)(path); Object.setPrototypeOf(file, (TFile as any).prototype); - (file as any).stat = { mtime: 1000, ctime: 1000 }; + file.stat = { mtime: 1000, ctime: 1000 }; return file; } return null; diff --git a/src/services/webViewerService/webViewerServiceHelpers.ts b/src/services/webViewerService/webViewerServiceHelpers.ts index 4ae16bca..2091ffa8 100644 --- a/src/services/webViewerService/webViewerServiceHelpers.ts +++ b/src/services/webViewerService/webViewerServiceHelpers.ts @@ -251,7 +251,7 @@ function tryExtractPluginApi(entry: unknown): WebViewerPluginApi | null { typeof maybe.openUrl === "function" || typeof maybe.handleOpenUrl === "function"; if (hasOpenCapability) { - return maybe as WebViewerPluginApi; + return maybe; } return null; } diff --git a/src/services/webViewerService/webViewerServiceSelection.ts b/src/services/webViewerService/webViewerServiceSelection.ts index e7acec19..f7e3b123 100644 --- a/src/services/webViewerService/webViewerServiceSelection.ts +++ b/src/services/webViewerService/webViewerServiceSelection.ts @@ -142,10 +142,12 @@ export class WebSelectionTracker { private scheduleNext(): void { if (!this.isRunning) return; - this.timeoutId = window.setTimeout(async () => { - await this.checkSelection(); - // Schedule next only after current check completes - this.scheduleNext(); + this.timeoutId = window.setTimeout(() => { + void (async () => { + await this.checkSelection(); + // Schedule next only after current check completes + this.scheduleNext(); + })(); }, this.intervalMs); } diff --git a/src/settings/model.test.ts b/src/settings/model.test.ts index 03d921c2..89470b93 100644 --- a/src/settings/model.test.ts +++ b/src/settings/model.test.ts @@ -185,7 +185,7 @@ describe("sanitizeSettings - legacy Miyo settings cleanup", () => { miyoRemoteVaultPath: "\\\\Mac\\Home\\Downloads\\graham-essays-main", }; - const sanitized = sanitizeSettings(legacySettings as any); + const sanitized = sanitizeSettings(legacySettings); expect(sanitized.enableMiyo).toBe(true); expect(sanitized.miyoServerUrl).toBe("http://127.0.0.1:8742"); @@ -209,7 +209,7 @@ describe("sanitizeSettings - legacy Miyo settings cleanup", () => { miyoRemoteVaultPath: "\\\\Mac\\Home\\Downloads\\graham-essays-main", }; - const sanitized = sanitizeSettings(legacySettings as any); + const sanitized = sanitizeSettings(legacySettings); const sanitizedRecord = sanitized as unknown as Record; expect(sanitized.userId).toBeTruthy(); diff --git a/src/settings/v2/SettingsMainV2.tsx b/src/settings/v2/SettingsMainV2.tsx index 4da22084..c1d3c1a9 100644 --- a/src/settings/v2/SettingsMainV2.tsx +++ b/src/settings/v2/SettingsMainV2.tsx @@ -86,8 +86,8 @@ const SettingsMainV2: React.FC = ({ plugin }) => { const [resetKey, setResetKey] = React.useState(0); const { latestVersion, hasUpdate } = useLatestVersion(plugin.manifest.version); - const handleReset = async () => { - const modal = new ResetSettingsConfirmModal(app, async () => { + const handleReset = () => { + const modal = new ResetSettingsConfirmModal(app, () => { resetSettings(); // Increment the key to force re-render of all components setResetKey((prev) => prev + 1); diff --git a/src/tools/ObsidianCliTools.ts b/src/tools/ObsidianCliTools.ts index 96319af2..e3b02319 100644 --- a/src/tools/ObsidianCliTools.ts +++ b/src/tools/ObsidianCliTools.ts @@ -41,7 +41,7 @@ export const obsidianDailyNoteTool = createLangChainTool({ func: async (args) => { const { command, vault } = args; - const params = buildCliParams(args as Record); + const params = buildCliParams(args); const result = await runObsidianCliCommand({ command, vault, params }); if (!result.ok) throwCliFailure(result); @@ -107,7 +107,7 @@ export const obsidianPropertiesTool = createLangChainTool({ throw new Error("name is required for property:read"); } - const params = buildCliParams(args as Record); + const params = buildCliParams(args); const result = await runObsidianCliCommand({ command, vault, params }); if (!result.ok) throwCliFailure(result); @@ -158,7 +158,7 @@ export const obsidianTasksTool = createLangChainTool({ schema: tasksSchema, func: async (args) => { const { command, vault } = args; - const params = buildCliParams(args as Record); + const params = buildCliParams(args); const result = await runObsidianCliCommand({ command, vault, params }); if (!result.ok) throwCliFailure(result); @@ -215,7 +215,7 @@ export const obsidianLinksTool = createLangChainTool({ schema: linksSchema, func: async (args) => { const { command, vault } = args; - const params = buildCliParams(args as Record); + const params = buildCliParams(args); const result = await runObsidianCliCommand({ command, vault, params }); if (!result.ok) throwCliFailure(result); @@ -262,7 +262,7 @@ export const obsidianTemplatesTool = createLangChainTool({ throw new Error("name is required for template:read"); } - const params = buildCliParams(args as Record); + const params = buildCliParams(args); const result = await runObsidianCliCommand({ command, vault, params }); if (!result.ok) throwCliFailure(result); @@ -343,7 +343,7 @@ export const obsidianBasesTool = createLangChainTool({ throw new Error(`file or path is required for ${command}`); } - const params = buildCliParams(args as Record); + const params = buildCliParams(args); const result = await runObsidianCliCommand({ command, vault, params }); if (!result.ok) throwCliFailure(result);