Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

V2 Plugin SDK

The V2 plugin system uses @hone/sdk to build native Hone plugins compiled by Perry and distributed via the marketplace.

Installation

npm install @hone/sdk

Plugin Lifecycle

  1. init – Host calls hone_plugin_init(host_api). The plugin stores the host API pointer for later use.
  2. activate – Host calls hone_plugin_activate(). The plugin initializes its state, registers commands, and sets up event handlers.
  3. hooks – Host calls hook methods as events occur (e.g., hone_plugin_on_document_format(event_ptr), hone_plugin_on_command(cmd_ptr)).
  4. deactivate – Host calls hone_plugin_deactivate(). The plugin cleans up resources.
  5. unload – Host calls dlclose(). The plugin binary is unloaded from memory.

Basic Plugin Structure

import { HonePlugin } from '@hone/sdk';
import type { HoneHost } from '@hone/sdk';

export class MyPlugin extends HonePlugin {
  activate(host: HoneHost): void {
    host.log('info', 'My plugin activated');

    host.commands.register('my-plugin.greet', 'Greet User', () => {
      host.ui.notify({ message: 'Hello from my plugin!' });
    });
  }

  deactivate(): void {
    // Clean up resources
  }
}

Host APIs

The HoneHost interface provides access to IDE functionality. Available APIs depend on declared capabilities – undeclared capabilities have null function pointers and will not be linked.

Editor

Requires editor.read and/or editor.write capabilities.

// Read buffer content
const text = host.editor.getBufferText(bufferId);
const lines = host.editor.getLines(bufferId, startLine, endLine);
const selection = host.editor.getSelection(bufferId);
const lineCount = host.editor.getLineCount(bufferId);

// Write edits (requires editor.write)
host.editor.submitEdits(bufferId, [
  { range: { start: { line: 0, character: 0 }, end: { line: 0, character: 5 } }, text: 'replacement' }
]);

Filesystem

Requires filesystem.read and/or filesystem.write capabilities.

// Read (patterns must match declared globs)
const content = host.fs.readText('/path/to/file.txt');
const exists = host.fs.exists('/path/to/file.txt');
const entries = host.fs.listDirectory('/path/to/dir');

// Write (requires filesystem.write)
host.fs.writeText('/path/to/file.txt', 'content');
host.fs.delete('/path/to/file.txt');
host.fs.createDirectory('/path/to/dir', { recursive: true });

UI

Requires various ui.* capabilities.

// Status bar (ui.statusbar)
const item = host.ui.createStatusBarItem({ text: 'My Plugin', alignment: 'left' });
host.ui.updateStatusBarItem(item, { text: 'Updated' });

// Panels (ui.panel)
const panel = host.ui.createPanel({
  title: 'My Panel',
  icon: 'list',
  content: [
    { type: 'Heading', text: 'Results', level: 2 },
    { type: 'List', items: ['Item 1', 'Item 2'] }
  ]
});

// Notifications (ui.notifications)
host.ui.notify({ message: 'Operation complete', type: 'info' });

// Command palette (ui.commandPalette)
host.commands.register('my-plugin.action', 'My Action', callback);

Process

Requires process.spawn capability with allowlisted binaries.

const result = host.process.spawn('git', ['status', '--porcelain'], {
  cwd: host.getWorkspacePath()
});

Network

Requires network capability (forces Tier 3 execution).

const response = host.network.httpRequest({
  method: 'GET',
  url: 'https://api.example.com/data',
  headers: { 'Authorization': 'Bearer token' }
});

Custom UI Panels

Plugins can create panels with structured content using PanelElement types:

ElementDescription
TextPlain or styled text
HeadingSection heading (levels 1-4)
ListOrdered or unordered list
TreeCollapsible tree structure
TableTabular data
InputText input field
ButtonClickable button
SeparatorVisual divider
ProgressProgress bar or spinner
CodeBlockSyntax-highlighted code
GroupContainer for other elements

Testing

Use MockHost and createTestBuffer from the SDK to test plugin logic without the full IDE:

import { MockHost, createTestBuffer } from '@hone/sdk';

const host = new MockHost();
const buffer = createTestBuffer('hello world');

const plugin = new MyPlugin();
plugin.activate(host);

// Assert plugin behavior against mock host

Compiling

Plugins are compiled to native shared libraries using Perry:

perry compile src/index.ts --output my-plugin

The output is a platform-specific shared library (.dylib on macOS, .dll on Windows, .so on Linux) that the Hone plugin host loads via dlopen/dlclose.

Perry AOT Constraints

Plugin code is compiled by Perry and must follow its constraints. Avoid:

  • Optional chaining (?.) – use explicit null checks
  • Nullish coalescing (??) – use if (x !== undefined)
  • Dynamic key access (obj[variable]) – use if/else if per key
  • for...of on arrays – use index-based for loops
  • ES6 shorthand properties ({ key }) – use { key: key }
  • Closures capturing this – use module-level functions and variables

See the Perry constraints reference for the full list.