Skip to main content

SDK Reference

Complete API reference for @practicalkit/plugin-sdk.

Installation

npm install @practicalkit/plugin-sdk

Initialization

import { PracticalKitSdk } from "@practicalkit/plugin-sdk";

const sdk = new PracticalKitSdk();
sdk.init();

// When your plugin is unloading
sdk.destroy();
Single Instance

Create only one SDK instance per module. Export it and import where needed.


Virtual File System (sdk.vfs)

Read and write files within the user's workspace.

vfs.exists(path)

Check if a file or directory exists.

const result = await sdk.vfs.exists("documents/notes.md");
// { exists: true, entryType: 'file' }

Returns: { exists: boolean, entryType: 'file' | 'directory' | null }

vfs.readDir(path)

List contents of a directory.

const entries = await sdk.vfs.readDir(".");
// [{ name: 'file.txt', type: 'file' }, { name: 'folder', type: 'directory' }]

Returns: DirectoryEntry[]

vfs.readFile(path)

Read file contents as a string.

const content = await sdk.vfs.readFile("notes.md");

Returns: string

vfs.writeFile(path, content)

Write content to a file. Creates the file if it doesn't exist.

await sdk.vfs.writeFile("output.txt", "Hello, world!");

vfs.rename(oldPath, newPath)

Rename or move a file/directory.

await sdk.vfs.rename("old-name.txt", "new-name.txt");

vfs.delete(path)

Delete a file or directory.

await sdk.vfs.delete("unwanted-file.txt");

vfs.createDir(path)

Create a directory.

await sdk.vfs.createDir("new-folder");

vfs.getAbsolutePath(path)

Get the absolute filesystem path for a relative path.

const absolutePath = await sdk.vfs.getAbsolutePath("notes.md");
// '/Users/alice/Documents/project/notes.md'

Workspace (sdk.workspace)

Information about the current workspace.

workspace.getDetails()

Get workspace information.

const details = await sdk.workspace.getDetails();
// { isOpen: true, path: '/Users/alice/Documents/project' }

workspace.onChanged(callback)

Listen for workspace changes (open/close).

const unsubscribe = sdk.workspace.onChanged((event) => {
console.log("Workspace changed:", event.path);
});

// Later: unsubscribe();

workspace.open()

Open a folder picker dialog.

const selectedPath = await sdk.workspace.open();
// '/Users/alice/Documents/new-project' or null if cancelled

Editor (sdk.editor)

Control the editor area.

editor.openFile(path)

Open a file in the editor.

await sdk.editor.openFile("readme.md");

File System Events (sdk.fs)

Subscribe to file change notifications.

fs.subscribe()

Start receiving file change events. Requires fs.watch permission.

await sdk.fs.subscribe();

fs.unsubscribe()

Stop receiving file change events.

await sdk.fs.unsubscribe();

fs.onFileChanged(callback)

Listen for file changes. Must call subscribe() first.

const unsubscribe = sdk.fs.onFileChanged((event) => {
console.log("File changed:", event.path, event.kind);
// kind: 'create' | 'modify' | 'remove'
});

Document Lifecycle (sdk.document)

For editor plugins that handle file content.

document.requestContent()

Get the document content when your editor opens.

const { content, hash } = await sdk.document.requestContent();

document.requestSave()

Trigger a save (e.g., when user presses Ctrl+S).

const newHash = await sdk.document.requestSave();

document.onSaveRequested(callback)

Handle save requests from the host.

sdk.document.onSaveRequested(async (payload) => {
// Return current content for saving
return getCurrentEditorContent();
});

document.onSaved(callback)

Notification when save completes.

sdk.document.onSaved((payload) => {
console.log("Saved successfully, new hash:", payload.hash);
});

UI (sdk.ui)

Control UI elements.

ui.getState()

Get current UI state. Requires ui:read permission.

const state = await sdk.ui.getState();
// { openTabs: [{ id: '...', title: 'file.md', isActive: true }] }

ui.setCollapseState(collapsed)

Expand or collapse your panel.

await sdk.ui.setCollapseState(false); // Expand
await sdk.ui.setCollapseState(true); // Collapse

ui.showContextMenu(options)

Display a context menu.

const result = await sdk.ui.showContextMenu({
x: event.clientX,
y: event.clientY,
items: [
{ id: "rename", label: "Rename" },
{ type: "separator" },
{ id: "delete", label: "Delete" },
],
});

if (result.selectedItemId === "rename") {
// Handle rename
}

ui.showNotification(message, type)

Show a toast notification.

sdk.ui.showNotification("File saved!", "success");
// type: 'info' | 'success' | 'warning' | 'error'

ui.confirm(options)

Show a confirmation dialog.

const confirmed = await sdk.ui.confirm({
title: "Delete file?",
message: "This action cannot be undone.",
okLabel: "Delete",
cancelLabel: "Cancel",
});

if (confirmed) {
// Proceed with deletion
}

ui.onTabsChanged(callback)

Listen for tab changes.

const unsubscribe = sdk.ui.onTabsChanged((event) => {
console.log("Open tabs:", event.payload.openTabs);
console.log("Has open tabs:", event.payload.hasOpenTabs);
});

Storage (sdk.storage)

Persist data for your plugin. Data is isolated per plugin.

Regular storage

For non-sensitive data.

// Store data
await sdk.storage.set(
"preferences",
new TextEncoder().encode(JSON.stringify(prefs))
);

// Retrieve data
const data = await sdk.storage.get("preferences");
if (data) {
const prefs = JSON.parse(new TextDecoder().decode(data));
}

// Delete data
await sdk.storage.delete("preferences");

Secret storage

For sensitive data (encrypted at rest).

// Store secret
await sdk.storage.setSecret("api-key", new TextEncoder().encode(apiKey));

// Retrieve secret
const data = await sdk.storage.getSecret("api-key");
if (data) {
const apiKey = new TextDecoder().decode(data);
}

// Delete secret
await sdk.storage.deleteSecret("api-key");
tip

All storage values are Uint8Array. Use TextEncoder/TextDecoder for strings.


Logging (sdk.logs)

Send logs to PracticalKit's log viewer.

sdk.logs.debug("Debugging info");
sdk.logs.info("Operation completed");
sdk.logs.warn("Something unexpected");
sdk.logs.error("Operation failed");

Named channels

Create separate log channels for different components.

const apiLogger = sdk.logs.createChannel("api");
apiLogger.info("Fetching data...");
// Shows as [api] Fetching data...

LLM Tools (sdk.llm)

Register tools that the AI assistant can use.

llm.registerTool(registration)

Register a new tool.

await sdk.llm.registerTool({
name: "searchFiles",
description: "Search for files by name pattern",
parameters: {
type: "object",
properties: {
pattern: {
type: "string",
description: 'The search pattern (e.g., "*.md")',
},
},
required: ["pattern"],
},
handler: async (args) => {
const { pattern } = args as { pattern: string };
// Perform search...
return { files: ["file1.md", "file2.md"] };
},
});

Tool registration fields:

FieldTypeRequiredDescription
namestringYesTool name (alphanumeric, underscores)
descriptionstringYesWhat the tool does (helps LLM decide when to use it)
parametersobjectYesJSON Schema for arguments
handlerfunctionYesAsync function that executes the tool
timeoutMsnumberNoExecution timeout in milliseconds

llm.unregisterTool(name)

Remove a registered tool.

await sdk.llm.unregisterTool("searchFiles");

llm.listTools()

List all available tools from all plugins.

const tools = await sdk.llm.listTools();

llm.executeTool(toolId, args)

Execute another plugin's tool.

const result = await sdk.llm.executeTool("file-explorer.readFile", {
path: "notes.md",
});

Plugin Messaging (sdk.plugin)

Communicate between modules within your plugin.

plugin.sendMessage(targetModuleId, message)

Send a message to another module.

const response = await sdk.plugin.sendMessage('background-service', {
action: 'sync',
data: { ... },
});

plugin.onMessage(handler)

Receive messages from other modules.

const unsubscribe = sdk.plugin.onMessage(async (payload) => {
console.log("From:", payload.sourceModuleId);
console.log("Message:", payload.message);
return { status: "ok" }; // Response sent back to sender
});

plugin.sendBroadcast(message)

Broadcast to all modules in your plugin.

await sdk.plugin.sendBroadcast({ type: "refresh" });

plugin.onBroadcast(handler)

Receive broadcasts.

const unsubscribe = sdk.plugin.onBroadcast((payload) => {
console.log("Broadcast:", payload.message);
});

Native Modules (sdk.native)

Call Rust-based native modules (advanced).

const result = await sdk.native.call(
"my-plugin", // plugin ID
"native-module", // module ID
"processFile", // command name
{ path: "data.bin" } // arguments
);

Types

The SDK exports useful types:

import {
PracticalKitSdk,
LogChannel,
type LlmToolHandler,
type LlmToolRegistration,
type DirectoryEntry,
type WorkspaceDetails,
// Valibot utilities for validation
safeParse,
type GenericSchema,
type InferOutput,
} from "@practicalkit/plugin-sdk";