Skip to content
Multilo Docs

Building an extension

Build your own extension against the multilo API: the anatomy, contribution points, activation, and publishing.

If a capability doesn’t exist yet, build it. A Multilo extension is real software that runs inside the desktop app’s extension host and programs against the global multilo API — the same way the built-in grammar, style, and citation tools do. First-party packs and community extensions share one runtime, one API, and one marketplace, so a community extension is a first-class citizen, not a second-class plugin.

Anatomy

An extension is two things:

  1. A manifest (manifest.json) declaring its identity, what it contributes, and when it activates. See the manifest reference.
  2. A module (your package.entryPoint) exporting activate(context) and, optionally, deactivate().

Your first extension

Everything you register goes on context.subscriptions so it is disposed when your extension deactivates.

extension.ts
/// <reference types="@multilo/extension-api" />

export function activate(context: ExtensionContext): void {
  context.subscriptions.push(
    multilo.commands.registerCommand("acme.hello", () =>
      multilo.window.showMessage("info", "Hello from my extension!"),
    ),
  );
}

export function deactivate(): void {}

Contribution points

Extensions extend the app through contribution points — scoped, permissioned surfaces you register against the multilo API:

  • Commands — register an action with multilo.commands.registerCommand and invoke it from menus, keybindings, or executeCommand.
  • Diagnostics — return Diagnostic[] for a document to surface inline issues, each with an optional suggestion the user can apply.
  • Configuration — read user settings with multilo.workspace.getConfiguration().get(key, fallback).

A diagnostic provider that flags unresolved TODOs:

diagnostics.ts
multilo.languages.registerDiagnosticProvider(
  { languages: ["markdown", "plaintext"], filePatterns: ["*.md", "*.txt"] },
  {
    provideDiagnostics(doc): Diagnostic[] {
      const out: Diagnostic[] = [];
      for (const m of doc.getText().matchAll(/\bTODO\b/g)) {
        out.push({
          kind: "todo",
          offset: m.index ?? 0,
          length: m[0].length,
          message: "unresolved TODO.",
          severity: multilo.DiagnosticSeverity.Info,
          ruleId: "acme-todo",
          suggestion: "Resolve or remove before publishing.",
        });
      }
      return out;
    },
  },
);

Activation events

Declare an activationEvents array so your extension only wakes when it is actually needed:

  • onStartupFinished — after the app finishes loading.
  • onLanguage:markdown — when a document of that language opens.
  • onCommand:<id> — when one of your commands runs.

Signing & publishing

  1. Build & test

    Develop against the SDK and load your extension in development to try it in the real app before you ship.
  2. Sign it

    Sign your extension so users can verify its integrity and trust before they install it.
  3. Publish

    Submit it from the publisher dashboard, where, once reviewed, users can discover and install it.

Same platform, top to bottom

Extensions run in an isolated, sandboxed runtime and are gated by the permissions they declare plus a verified-trust check. See the manifest & permissions reference and the extensions overview.