Extending Citet

Plugins

Citet has a typed plugin platform. Plugins contribute commands and panels, read the project, publish editor annotations and diagnostics, and propose file changes — all through an explicit capability interface rather than by reaching into the app's internals.

Built-in plugins

The current runtime is trusted and bundled with the app. Two first-party plugins ship today:

→AI review
The plugin behind the AI assistant. It contributes the assistant commands and panel; the web app supplies the model runtime and provider credentials.
→Test lab
A trusted internal plugin used to exercise editor navigation, PDF jumps, diagnostics, decorations, hovers, text edits, project-file proposals, AI calls, and citation search. It is a working reference for what the plugin API can do.

Capabilities and permissions

A plugin declares the capabilities it needs in its manifest, and the host only exposes the matching APIs. If a capability is not requested, it is not available to the plugin. The capability surface covers:

→Project text & files
Read open documents and the file tree; propose file changes (writes are never direct).
→Editor
Decorations, annotations in the gutter rail, and hover providers.
→Diagnostics
Publish problems into the diagnostics panel.
→PDF & workspace
React to the active document, selection, and viewer state.
→Citations
Search the project's citation library.
→AI
Request a model run through the host runtime.

File writes are proposal-based

Plugins cannot mutate your files directly. They produce a typed changeset with proposeChanges(...), and the host applies it only after you approve the preview. AI-backed plugins run the model through the host and return their results through the same proposal flow.

Writing a plugin

Plugin authors depend on @citet/plugin-core — the typed interface shared by the host and plugin packages — declare a manifest with a publisher, name, version, and the permissions they need, then activate against the provided context.

import { defineCitetPlugin, citetCapabilities } from "@citet/plugin-core";

export default defineCitetPlugin({
  manifest: {
    publisher: "acme",
    name: "paper-tools",
    displayName: "Paper Tools",
    version: "0.1.0",
    permissions: [
      citetCapabilities.projectText.read,
      citetCapabilities.diagnostics.publish,
      citetCapabilities.editor.annotations,
    ],
  },
  activate: (ctx) => {
    ctx.workspace.text.onActiveDocumentChanged(async (doc) => {
      if (!doc) return;
      ctx.diagnostics.publish([
        { id: "paper-tools.ready", severity: "info", message: "Paper Tools active." },
      ]);
    });
  },
});

Commands and views use the same handle pattern: you define contribution handles once and pass them back to the runtime, and the host derives stable ids internally — plugin code never hard-codes global id strings.

▶Marketplace-ready by design

Although v1 plugins are trusted and bundled, the package layout is intentionally built for third-party distribution. Plugins depend only on @citet/plugin-core, declare a manifest, request explicit permissions, and must avoid importing app internals such as Dockview, CodeMirror, Yjs, Drizzle, or router state. That boundary is what will let untrusted plugins run safely in a later release.