Skip to content

Alma Plugin API Reference ​

Complete reference for all APIs available to Alma plugins.

PluginContext ​

The PluginContext object is passed to your plugin's activate function and provides access to all Alma APIs.

typescript
interface PluginContext {
    // Plugin info
    readonly id: string;
    readonly extensionPath: string;
    readonly storagePath: string;
    readonly globalStoragePath: string;

    // APIs
    readonly logger: Logger;
    readonly storage: StorageAPIs;
    readonly tools: ToolsAPI;
    readonly commands: CommandsAPI;
    readonly events: EventsAPI;
    readonly ui: UIAPI;
    readonly chat: ChatAPI;
    readonly providers: ProvidersAPI;
    readonly workspace: WorkspaceAPI;
    readonly settings: SettingsAPI;
    readonly i18n: I18nAPI;
}

Context Properties ​

PropertyTypeDescription
idstringUnique plugin identifier
extensionPathstringAbsolute path to plugin directory
storagePathstringPath for plugin-specific local storage
globalStoragePathstringPath for global plugin storage

Logger API ​

Logging utilities for debugging and monitoring.

typescript
interface Logger {
    info(message: string, ...args: unknown[]): void;
    warn(message: string, ...args: unknown[]): void;
    error(message: string, ...args: unknown[]): void;
    debug(message: string, ...args: unknown[]): void;
}

Usage ​

typescript
const { logger } = context;

logger.info('Plugin started');
logger.debug('Processing data:', { key: 'value' });
logger.warn('Deprecated API used');
logger.error('Failed to connect:', error);

Storage API ​

Persistent key-value storage for plugin data.

Local Storage ​

typescript
interface Storage {
    get<T>(key: string): Promise<T | undefined>;
    get<T>(key: string, defaultValue: T): Promise<T>;
    set(key: string, value: unknown): Promise<void>;
    delete(key: string): Promise<void>;
    keys(): Promise<string[]>;
    clear(): Promise<void>;
}

Secret Storage ​

For sensitive data like API keys:

typescript
interface SecretStorage {
    get(key: string): Promise<string | undefined>;
    set(key: string, value: string): Promise<void>;
    delete(key: string): Promise<void>;
}

Usage ​

typescript
const { storage } = context;

// Local storage
await storage.local.set('lastRun', Date.now());
const lastRun = await storage.local.get<number>('lastRun', 0);

// Workspace storage (per-project)
await storage.workspace.set('projectConfig', { theme: 'dark' });

// Secret storage (encrypted)
await storage.secrets.set('apiKey', 'sk-xxx...');
const apiKey = await storage.secrets.get('apiKey');

Tools API ​

Register tools that the AI assistant can use.

typescript
interface ToolsAPI {
    register<TParams extends z.ZodType>(
        id: string,
        definition: ToolDefinition<TParams>
    ): Disposable;
    unregister(id: string): void;
}

interface ToolDefinition<TParams extends z.ZodType> {
    description: string;
    parameters: TParams;
    outputSafetyMode?: 'exact' | 'compact' | 'passthrough';
    execute: (params: z.infer<TParams>, context: ToolContext) => Promise<unknown>;
}

interface ToolContext {
    threadId: string;
    messageId: string;
    abortSignal?: AbortSignal;
}

Usage ​

typescript
import { z } from 'zod';

const { tools, logger } = context;

const disposable = tools.register('weather', {
    description: 'Get current weather for a location',
    parameters: z.object({
        city: z.string().describe('City name'),
        units: z.enum(['celsius', 'fahrenheit']).default('celsius'),
    }),
    execute: async (params, ctx) => {
        logger.info(`Getting weather for ${params.city}`);

        // Check for cancellation
        if (ctx.abortSignal?.aborted) {
            throw new Error('Request cancelled');
        }

        // Fetch weather data...
        return {
            city: params.city,
            temperature: 22,
            units: params.units,
            condition: 'sunny',
        };
    },
});

// Remember to dispose when plugin deactivates
disposable.dispose();

Commands API ​

Register commands that users can invoke from the command palette.

typescript
interface CommandsAPI {
    register(
        id: string,
        handler: (...args: unknown[]) => Promise<unknown> | unknown
    ): Disposable;
    execute<T>(id: string, ...args: unknown[]): Promise<T>;
}

Usage ​

typescript
const { commands, ui } = context;

// Register a command
const disposable = commands.register('myPlugin.sayHello', async () => {
    ui.showNotification('Hello!');
});

// Execute another command
await commands.execute('alma.openSettings');

Events API ​

Subscribe to lifecycle events and hooks.

typescript
interface EventsAPI {
    on<T extends HookName>(
        hookName: T,
        handler: HookHandler<T>,
        options?: { priority?: number }
    ): Disposable;
    once<T extends HookName>(
        hookName: T,
        handler: HookHandler<T>
    ): Disposable;
}

See Hooks & Events Guide for detailed documentation.


UI API ​

User interface utilities.

typescript
interface UIAPI {
    showNotification(message: string, options?: NotificationOptions): void;
    showError(message: string): void;
    showWarning(message: string): void;
    showQuickPick<T>(items: QuickPickItem<T>[], options?: QuickPickOptions): Promise<T | undefined>;
    showInputBox(options?: InputBoxOptions): Promise<string | undefined>;
    showConfirmDialog(message: string, options?: ConfirmOptions): Promise<boolean>;
    withProgress<T>(options: ProgressOptions, task: ProgressTask<T>): Promise<T>;
    createStatusBarItem(options: StatusBarItemOptions): StatusBarItem;
    readonly theme: {
        current: Theme;
        onChange: Event<Theme>;
    };
}

See UI API Guide for detailed documentation.


Chat API ​

Access chat threads and messages.

typescript
interface ChatAPI {
    getThread(id: string): Promise<Thread | undefined>;
    getActiveThread(): Promise<Thread | undefined>;
    createThread(options?: { title?: string; model?: string }): Promise<Thread>;
    getMessages(threadId: string): Promise<Message[]>;
}

interface Thread {
    id: string;
    title: string;
    model?: string;
    createdAt: string;
    updatedAt: string;
}

interface Message {
    id: string;
    threadId: string;
    role: 'user' | 'assistant' | 'system';
    content: UIMessage; // The stored message object ({ id, role, parts, metadata })
    createdAt: string;
}

getActiveThread() returns the thread the user currently has open (the last one the app activated), or undefined before any thread has been opened.

Usage ​

typescript
const { chat, logger } = context;

// Get the current thread
const activeThread = await chat.getActiveThread();
if (activeThread) {
    logger.info(`Current thread: ${activeThread.title}`);

    // Get all messages
    const messages = await chat.getMessages(activeThread.id);
    logger.info(`Thread has ${messages.length} messages`);
}

// Create a new thread
const newThread = await chat.createThread({
    title: 'New Conversation',
    model: 'claude-3-opus',
});

Tool Result Safety ​

Alma can compact large tool outputs before they are fed back into the model. Plugin tools can opt into a specific behavior with outputSafetyMode.

typescript
type ToolResultSafetyMode = 'exact' | 'compact' | 'passthrough';
ModeUse ForBehavior
exactStructured results, canonical source text, IDs Alma or the model must use exactlyOutput is returned unchanged
compactLarge observational output such as logs, search results, scraped content, diagnosticsAlma may truncate large text fields to reduce token usage
passthroughBinary-heavy payloads such as screenshots or base64 blobsAlma skips compaction entirely

Guidance:

  • Use exact when later tool calls depend on byte-for-byte stability.
  • Use compact only for lossy-safe output where summaries or truncation are acceptable.
  • Use passthrough for image or binary payloads that should not be modified.
  • If omitted, Alma defaults unknown plugin tools to a safe exact mode.
typescript
const disposable = tools.register('readRepoFile', {
    description: 'Read repository file contents exactly',
    outputSafetyMode: 'exact',
    parameters: z.object({
        path: z.string(),
    }),
    execute: async ({ path }) => {
        return {
            path,
            content: await fs.readFile(path, 'utf8'),
        };
    },
});
typescript
const disposable = tools.register('tailLogs', {
    description: 'Return recent service logs',
    outputSafetyMode: 'compact',
    parameters: z.object({
        service: z.string(),
    }),
    execute: async ({ service }) => {
        return {
            service,
            stdout: await getRecentLogs(service),
        };
    },
});

As a fallback, Alma also understands a description directive:

typescript
tools.register('readRepoFile', {
    description: 'Read repository file contents exactly [alma-output-safety: exact]',
    parameters: z.object({ path: z.string() }),
    execute: async ({ path }) => ({ path, content: await fs.readFile(path, "utf8") }),
});

Prefer the explicit outputSafetyMode field for new plugins.


Providers API ​

Access and register AI providers.

typescript
interface ProvidersAPI {
    list(): Promise<Provider[]>;
    get(id: string): Promise<Provider | undefined>;
    register(provider: ProviderDefinition): Disposable;
}

interface Provider {
    id: string;
    name: string;
    type: string;
    enabled: boolean;
}

interface ProviderDefinition {
    id: string;
    name: string;
    icon?: string;
    getModels(): Promise<Array<{ id: string; name: string }>>;
    createChatCompletion(request: {
        model: string;
        messages: Message[];
        stream?: boolean;
    }): Promise<ReadableStream | { content: string }>;
}

Usage ​

typescript
const { providers, logger } = context;

// List all providers
const allProviders = await providers.list();
for (const provider of allProviders) {
    logger.info(`Provider: ${provider.name} (${provider.enabled ? 'enabled' : 'disabled'})`);
}

// Register a custom provider
const disposable = providers.register({
    id: 'my-provider',
    name: 'My Custom Provider',
    async getModels() {
        return [
            { id: 'model-1', name: 'Model One' },
            { id: 'model-2', name: 'Model Two' },
        ];
    },
    async createChatCompletion(request) {
        // Implement chat completion...
        return { content: 'Response from my provider' };
    },
});

Workspace API ​

File system operations (requires appropriate permissions).

typescript
interface WorkspaceAPI {
    readonly rootPath: string | undefined;
    readonly workspaceFolders: WorkspaceFolder[];
    readFile(filePath: string): Promise<Uint8Array>;
    writeFile(filePath: string, content: Uint8Array): Promise<void>;
    stat(filePath: string): Promise<FileStat>;
    readDirectory(dirPath: string): Promise<[string, FileType][]>;
    createFileSystemWatcher(pattern: string): FileSystemWatcher;
}

interface WorkspaceFolder {
    id: string;
    path: string;
    name: string;
}

interface FileStat {
    type: 'file' | 'directory' | 'symlink';
    size: number;
    mtime: number;
    ctime: number;
}

type FileType = 'file' | 'directory' | 'symlink' | 'unknown';

interface FileSystemWatcher extends Disposable {
    onDidCreate: Event<string>;
    onDidChange: Event<string>;
    onDidDelete: Event<string>;
}

Usage ​

typescript
const { workspace, logger } = context;

// Read a file
const content = await workspace.readFile('/path/to/file.txt');
const text = new TextDecoder().decode(content);

// Write a file
const data = new TextEncoder().encode('Hello, World!');
await workspace.writeFile('/path/to/output.txt', data);

// Watch for file changes
const watcher = workspace.createFileSystemWatcher('**/*.ts');
watcher.onDidChange((path) => {
    logger.info(`File changed: ${path}`);
});

Settings API ​

Read and write plugin settings.

typescript
interface SettingsAPI {
    get<T>(key: string): T | undefined;
    get<T>(key: string, defaultValue: T): T;
    update(key: string, value: unknown): Promise<void>;
    onDidChange: Event<SettingsChangeEvent>;
}

interface SettingsChangeEvent {
    key: string;
    oldValue: unknown;
    newValue: unknown;
}

Usage ​

typescript
const { settings, logger } = context;

// Read a setting with default value
const enabled = settings.get<boolean>('myPlugin.enabled', true);
const threshold = settings.get<number>('myPlugin.threshold', 100);

// Update a setting
await settings.update('myPlugin.lastRun', Date.now());

// React to setting changes
const disposable = settings.onDidChange((event) => {
    if (event.key === 'myPlugin.enabled') {
        logger.info(`Plugin ${event.newValue ? 'enabled' : 'disabled'}`);
    }
});

I18n API ​

Internationalization support.

typescript
interface I18nAPI {
    t(key: string, params?: Record<string, unknown>): string;
    locale: string;
    onDidChangeLocale: Event<string>;
}

Usage ​

typescript
const { i18n, logger } = context;

// Get current locale
logger.info(`Current locale: ${i18n.locale}`);

// Translate a string
const greeting = i18n.t('hello', { name: 'World' });

// React to locale changes
i18n.onDidChangeLocale((newLocale) => {
    logger.info(`Locale changed to: ${newLocale}`);
});

Disposable Pattern ​

Many API methods return a Disposable object. Always dispose of resources when your plugin deactivates:

typescript
interface Disposable {
    dispose(): void;
}

Plugin dispose() ​

The dispose() returned from activate() runs when the plugin is disabled, reloaded, updated or uninstalled. It may return a Promise: Alma awaits it (up to 5 seconds) before unregistering the plugin's hooks and, on update or uninstall, before removing its files — so pending writes can be flushed here. A rejected or timed-out dispose() is logged and deactivation continues.

typescript
interface PluginActivation {
    dispose(): void | Promise<void>;
}

For flushing on app exit use the app.willQuit hook instead — dispose() is not called when Alma quits.

Best Practice ​

typescript
export async function activate(context: PluginContext): Promise<PluginActivation> {
    const disposables: Disposable[] = [];

    // Collect all disposables
    disposables.push(
        context.commands.register('myCommand', () => {}),
        context.events.on('app.ready', () => {}),
        context.settings.onDidChange(() => {})
    );

    return {
        dispose: () => {
            // Dispose all at once
            disposables.forEach(d => d.dispose());
        },
    };
}

Type Definitions ​

Install the type definitions package for full TypeScript support:

bash
npm install alma-plugin-api

Then import types:

typescript
import type {
    PluginContext,
    PluginActivation,
    Disposable,
    Logger,
    Storage,
    SecretStorage,
    ToolsAPI,
    ToolDefinition,
    ToolContext,
    CommandsAPI,
    EventsAPI,
    HookName,
    HookHandler,
    HookInput,
    HookOutput,
    UIAPI,
    NotificationOptions,
    StatusBarItem,
    ChatAPI,
    Thread,
    Message,
    ProvidersAPI,
    Provider,
    WorkspaceAPI,
    SettingsAPI,
    I18nAPI,
} from 'alma-plugin-api';