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();
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");
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:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Tool name (alphanumeric, underscores) |
description | string | Yes | What the tool does (helps LLM decide when to use it) |
parameters | object | Yes | JSON Schema for arguments |
handler | function | Yes | Async function that executes the tool |
timeoutMs | number | No | Execution 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";