# Plugin Ideas From OpenCode

Status: design report, not a V1 commitment

Paperclip V1 explicitly excludes a plugin framework in [doc/SPEC-implementation.md](../SPEC-implementation.md), but the long-horizon spec says the architecture should leave room for extensions. This report studies the `opencode` plugin system and translates the useful patterns into a Paperclip-shaped design.

Assumption for this document: Paperclip is a single-tenant operator-controlled instance. Plugin installation should therefore be global across the instance. "Companies" are still first-class Paperclip objects, but they are organizational records, not tenant-isolation boundaries for plugin trust or installation.

## Executive Summary

`opencode` has a real plugin system already. It is intentionally low-friction:

- plugins are plain JS/TS modules
- they load from local directories and npm packages
- they can hook many runtime events
- they can add custom tools
- they can extend provider auth flows
- they run in-process and can mutate runtime behavior directly

That model works well for a local coding tool. It should not be copied literally into Paperclip.

The main conclusion is:

- Paperclip should copy `opencode`'s typed SDK, deterministic loading, low authoring friction, and clear extension surfaces.
- Paperclip should not copy `opencode`'s trust model, project-local plugin loading, "override by name collision" behavior, or arbitrary in-process mutation hooks for core business logic.
- Paperclip should use multiple extension classes instead of one generic plugin bag:
  - trusted in-process modules for low-level platform concerns like agent adapters, storage providers, secret providers, and possibly run-log backends
  - out-of-process plugins for most third-party integrations like Linear, GitHub Issues, Grafana, Stripe, and schedulers
  - schema-driven UI contributions for dashboard widgets, settings panels, and company pages
  - a typed event bus plus scheduled jobs for automation

If Paperclip does this well, the examples you listed become straightforward:

- file browser / terminal / git workflow / child process tracking become workspace-runtime plugins built on first-party primitives
- Linear / GitHub / Grafana / Stripe become connector plugins
- future knowledge base and accounting features can also fit the same model

## Sources Examined

I cloned `anomalyco/opencode` and reviewed commit:

- `a965a062595403a8e0083e85770315d5dc9628ab`

Primary files reviewed:

- `https://github.com/anomalyco/opencode/blob/a965a062595403a8e0083e85770315d5dc9628ab/packages/plugin/src/index.ts`
- `https://github.com/anomalyco/opencode/blob/a965a062595403a8e0083e85770315d5dc9628ab/packages/plugin/src/tool.ts`
- `https://github.com/anomalyco/opencode/blob/a965a062595403a8e0083e85770315d5dc9628ab/packages/opencode/src/plugin/index.ts`
- `https://github.com/anomalyco/opencode/blob/a965a062595403a8e0083e85770315d5dc9628ab/packages/opencode/src/config/config.ts`
- `https://github.com/anomalyco/opencode/blob/a965a062595403a8e0083e85770315d5dc9628ab/packages/opencode/src/tool/registry.ts`
- `https://github.com/anomalyco/opencode/blob/a965a062595403a8e0083e85770315d5dc9628ab/packages/opencode/src/provider/auth.ts`
- `https://github.com/anomalyco/opencode/blob/a965a062595403a8e0083e85770315d5dc9628ab/packages/web/src/content/docs/plugins.mdx`
- `https://github.com/anomalyco/opencode/blob/a965a062595403a8e0083e85770315d5dc9628ab/packages/web/src/content/docs/custom-tools.mdx`
- `https://github.com/anomalyco/opencode/blob/a965a062595403a8e0083e85770315d5dc9628ab/packages/web/src/content/docs/ecosystem.mdx`

Relevant Paperclip files reviewed for current extension seams:

- [server/src/adapters/registry.ts](../../server/src/adapters/registry.ts)
- [ui/src/adapters/registry.ts](../../ui/src/adapters/registry.ts)
- [server/src/storage/provider-registry.ts](../../server/src/storage/provider-registry.ts)
- [server/src/secrets/provider-registry.ts](../../server/src/secrets/provider-registry.ts)
- [server/src/services/run-log-store.ts](../../server/src/services/run-log-store.ts)
- [server/src/services/activity-log.ts](../../server/src/services/activity-log.ts)
- [doc/SPEC.md](../SPEC.md)
- [doc/SPEC-implementation.md](../SPEC-implementation.md)

## What OpenCode Actually Implements

## 1. Plugin authoring API

`opencode` exposes a small package, `@opencode-ai/plugin`, with a typed `Plugin` function and a typed `tool()` helper.

Core shape:

- a plugin is an async function that receives a context object
- the plugin returns a `Hooks` object
- hooks are optional
- plugins can also contribute tools and auth providers

The plugin init context includes:

- an SDK client
- current project info
- current directory
- current git worktree
- server URL
- Bun shell access

That is important: `opencode` gives plugins rich runtime power immediately, not a narrow capability API.

## 2. Hook model

The hook set is broad. It includes:

- event subscription
- config-time hook
- message hooks
- model parameter/header hooks
- permission decision hooks
- shell env injection
- tool execution before/after hooks
- tool definition mutation
- compaction prompt customization
- text completion transforms

The implementation pattern is very simple:

- core code constructs an `output` object
- each matching plugin hook runs sequentially
- hooks mutate the `output`
- final mutated output is used by core

This is elegant and easy to extend.

It is also extremely powerful. A plugin can change auth headers, model params, permission answers, tool inputs, tool descriptions, and shell environment.

## 3. Plugin discovery and load order

`opencode` supports two plugin sources:

- local files
- npm packages

Local directories:

- `~/.config/opencode/plugins/`
- `.opencode/plugins/`

Npm plugins:

- listed in config under `plugin: []`

Load order is deterministic and documented:

1. global config
2. project config
3. global plugin directory
4. project plugin directory

Important details:

- config arrays are concatenated rather than replaced
- duplicate plugin names are deduplicated with higher-precedence entries winning
- internal first-party plugins and default plugins are also loaded through the plugin pipeline

This gives `opencode` a real precedence model rather than "whatever loaded last by accident."

## 4. Dependency handling

For local config/plugin directories, `opencode` will:

- ensure a `package.json` exists
- inject `@opencode-ai/plugin`
- run `bun install`

That lets local plugins and local custom tools import dependencies.

This is excellent for local developer ergonomics.

It is not a safe default for an operator-controlled control plane server.

## 5. Error handling

Plugin load failures do not hard-crash the runtime by default.

Instead, `opencode`:

- logs the error
- publishes a session error event
- continues loading other plugins

That is a good operational pattern. One bad plugin should not brick the entire product unless the operator has explicitly configured it as required.

## 6. Tools are a first-class extension point

`opencode` has two ways to add tools:

- export tools directly from a plugin via `hook.tool`
- define local files in `.opencode/tools/` or global tools directories

The tool API is strong:

- tools have descriptions
- tools have Zod schemas
- tool execution gets context like session ID, message ID, directory, and worktree
- tools are merged into the same registry as built-in tools
- tool definitions themselves can be mutated by a `tool.definition` hook

The most aggressive part of the design:

- custom tools can override built-in tools by name

That is very powerful for a local coding assistant.
It is too dangerous for Paperclip core actions.

## 7. Auth is also a plugin surface

`opencode` allows plugins to register auth methods for providers.

A plugin can contribute:

- auth method metadata
- prompt flows
- OAuth flows
- API key flows
- request loaders that adapt provider behavior after auth succeeds

This is a strong pattern worth copying. Integrations often need custom auth UX and token handling.

## 8. Ecosystem evidence

The ecosystem page is the best proof that the model is working in practice.
Community plugins already cover:

- sandbox/workspace systems
- auth providers
- session headers / telemetry
- memory/context features
- scheduling
- notifications
- worktree helpers
- background agents
- monitoring

That validates the main thesis: a simple typed plugin API can create real ecosystem velocity.

## What OpenCode Gets Right

## 1. Separate plugin SDK from host runtime

This is one of the best parts of the design.

- plugin authors code against a clean public package
- host internals can evolve behind the loader
- runtime code and plugin code have a clean contract boundary

Paperclip should absolutely do this.

## 2. Deterministic loading and precedence

`opencode` is explicit about:

- where plugins come from
- how config merges
- what order wins

Paperclip should copy this discipline.

## 3. Low-ceremony authoring

A plugin author does not have to learn a giant framework.

- export async function
- return hooks
- optionally export tools

That simplicity matters.

## 4. Typed tool definitions

The `tool()` helper is excellent:

- typed
- schema-based
- easy to document
- easy for runtime validation

Paperclip should adopt this style for plugin actions, automations, and UI schemas.

## 5. Built-in features and plugins use similar shapes

`opencode` uses the same hook system for internal and external plugin-style behavior in several places.
That reduces special cases.

Paperclip can benefit from that with adapters, secret backends, storage providers, and connector modules.

## 6. Incremental extension, not giant abstraction upfront

`opencode` did not design a giant marketplace platform first.
It added concrete extension points that real features needed.

That is the correct mindset for Paperclip too.

## What Paperclip Should Not Copy Directly

## 1. In-process arbitrary plugin code as the default

`opencode` is basically a local agent runtime, so unsandboxed plugin execution is acceptable for its audience.

Paperclip is a control plane for an operator-managed instance with company objects.
The risk profile is different:

- secrets matter
- approval gates matter
- budgets matter
- mutating actions require auditability

Default third-party plugins should not run with unrestricted in-process access to server memory, DB handles, and secrets.

## 2. Project-local plugin loading

`opencode` has project-local plugin folders because the tool is centered around a codebase.

Paperclip is not project-scoped. It is instance-scoped.
The comparable unit is:

- instance-installed plugin package

Paperclip should not auto-load arbitrary code from a workspace repo like `.paperclip/plugins` or project directories.

## 3. Arbitrary mutation hooks on core business decisions

Hooks like:

- `permission.ask`
- `tool.execute.before`
- `chat.headers`
- `shell.env`

make sense in `opencode`.

For Paperclip, equivalent hooks into:

- approval decisions
- issue checkout semantics
- activity log behavior
- budget enforcement

would be a mistake.

Core invariants should stay in core code, not become hook-rewritable.

## 4. Override-by-name collision

Allowing a plugin to replace a built-in tool by name is useful in a local agent product.

Paperclip should not allow plugins to silently replace:

- core routes
- core mutating actions
- auth behaviors
- permission evaluators
- budget logic
- audit logic

Extension should be additive or explicitly delegated, never accidental shadowing.

## 5. Auto-install and execute from user config

`opencode`'s "install dependencies at startup" flow is ergonomic.
For Paperclip it would be risky because it combines:

- package installation
- code loading
- execution

inside the control-plane server startup path.

Paperclip should require an explicit operator install step.

## Why Paperclip Needs A Different Shape

The products are solving different problems.

| Topic | OpenCode | Paperclip |
|---|---|---|
| Primary unit | local project/worktree | single-tenant operator instance with company objects |
| Trust assumption | local power user on own machine | operator managing one trusted Paperclip instance |
| Failure blast radius | local session/runtime | entire company control plane |
| Extension style | mutate runtime behavior freely | preserve governance and auditability |
| UI model | local app can load local behavior | board UI must stay coherent and safe |
| Security model | host-trusted local plugins | needs capability boundaries and auditability |

That means Paperclip should borrow the good ideas from `opencode` but use a stricter architecture.

## Paperclip Already Has Useful Pre-Plugin Seams

Paperclip has several extension-like seams already:

- server adapter registry: [server/src/adapters/registry.ts](../../server/src/adapters/registry.ts)
- UI adapter registry: [ui/src/adapters/registry.ts](../../ui/src/adapters/registry.ts)
- storage provider registry: [server/src/storage/provider-registry.ts](../../server/src/storage/provider-registry.ts)
- secret provider registry: [server/src/secrets/provider-registry.ts](../../server/src/secrets/provider-registry.ts)
- pluggable run-log store seam: [server/src/services/run-log-store.ts](../../server/src/services/run-log-store.ts)
- activity log and live event emission: [server/src/services/activity-log.ts](../../server/src/services/activity-log.ts)

This is good news.
Paperclip does not need to invent extensibility from scratch.
It needs to unify and harden existing seams.

## Recommended Paperclip Plugin Model

## 1. Use multiple extension classes

Do not create one giant `hooks` object for everything.

Use distinct plugin classes with different trust models.

| Extension class | Examples | Runtime model | Trust level | Why |
|---|---|---|---|---|
| Platform module | agent adapters, storage providers, secret providers, run-log backends | in-process | highly trusted | tight integration, performance, low-level APIs |
| Connector plugin | Linear, GitHub Issues, Grafana, Stripe | out-of-process worker or sidecar | medium | external sync, safer isolation, clearer failure boundary |
| Workspace plugin | file browser, terminal, git workflow, child process/server tracking | mixed: core workspace services plus plugin descriptors | high | needs local OS/workspace primitives but should reuse core services |
| UI contribution | dashboard widgets, settings forms, company panels | schema-driven first, remote React later if needed | medium | safer than arbitrary frontend code |
| Automation plugin | alerts, schedulers, sync jobs, webhook processors | out-of-process | medium | event-driven automation is a natural plugin fit |

This split is the most important design recommendation in this report.

## 2. Keep low-level modules separate from third-party plugins

Paperclip already has this pattern implicitly:

- adapters are one thing
- storage providers are another
- secret providers are another

Keep that separation.

I would formalize it like this:

- `module` means trusted code loaded by the host for low-level runtime services
- `plugin` means integration code that talks to Paperclip through a typed plugin protocol and capability model

This avoids trying to force Stripe, a PTY terminal, and a new agent adapter into the same abstraction.

## 3. Prefer event-driven extensions over core-logic mutation

For third-party plugins, the primary API should be:

- subscribe to typed domain events
- read instance state, including company-bound business records when relevant
- register webhooks
- run scheduled jobs
- write plugin-owned state
- add additive UI surfaces
- invoke explicit Paperclip actions through the API

Do not make third-party plugins responsible for:

- deciding whether an approval passes
- intercepting issue checkout semantics
- rewriting activity log behavior
- overriding budget hard-stops

Those are core invariants.

## 4. Start with schema-driven UI contributions

Arbitrary third-party React bundles inside the board UI are possible later, but they should not be the first version.

First version should let plugins contribute:

- settings sections defined by JSON schema
- dashboard widgets with server-provided data
- sidebar entries with fixed shell rendering
- detail-page tabs that render plugin data through core UI components

Why:

- simpler to secure
- easier to keep visually coherent
- easier to preserve context and auditability
- easier to test

Later, if needed, Paperclip can support richer frontend extensions through versioned remote modules.

## 5. Make installation global and keep mappings/config separate

`opencode` is mostly user-level local config.
Paperclip should treat plugin installation as a global instance-level action.

Examples:

- install `@paperclip/plugin-linear` once
- make it available everywhere immediately
- optionally store mappings over Paperclip objects if one company maps to a different Linear team than another

## 6. Use project workspaces as the primary anchor for local tooling

Paperclip already has a concrete workspace model for projects:

- projects expose `workspaces` and `primaryWorkspace`
- the database already has `project_workspaces`
- project routes already support creating, updating, and deleting workspaces
- heartbeat resolution already prefers project workspaces before falling back to task-session or agent-home workspaces

That means local/runtime plugins should generally anchor themselves to projects first, not invent a parallel workspace model.

Practical guidance:

- file browser should browse project workspaces first
- terminal sessions should be launchable from a project workspace
- git should treat the project workspace as the repo root anchor
- dev server and child-process tracking should attach to project workspaces
- issue and agent views can still deep-link into the relevant project workspace context

In other words:

- `project` is the business object
- `project_workspace` is the local runtime anchor
- plugins should build on that instead of creating an unrelated workspace model first

## A Concrete SDK Shape For Paperclip

An intentionally narrow first pass could look like this:

```ts
import { definePlugin, z } from "@paperclipai/plugin-sdk";

export default definePlugin({
  id: "@paperclip/plugin-linear",
  version: "0.1.0",
  kind: ["connector", "ui"],
  capabilities: [
    "events.subscribe",
    "jobs.schedule",
    "http.outbound",
    "instance.settings",
    "dashboard.widget",
    "secrets.read-ref",
  ],
  instanceConfigSchema: z.object({
    linearBaseUrl: z.string().url().optional(),
    companyMappings: z.array(
      z.object({
        companyId: z.string(),
        teamId: z.string(),
        apiTokenSecretRef: z.string(),
      }),
    ).default([]),
  }),
  async register(ctx) {
    ctx.jobs.register("linear-pull", { cron: "*/5 * * * *" }, async (job) => {
      // sync Linear issues into plugin-owned state or explicit Paperclip entities
    });

    ctx.events.on("issue.created", async (event) => {
      // optional outbound sync
    });

    ctx.ui.registerDashboardWidget({
      id: "linear-health",
      title: "Linear",
      loader: async ({ companyId }) => ({ status: "ok" }),
    });
  },
});
```

The important point is not the exact syntax.
The important point is the contract shape:

- typed manifest
- explicit capabilities
- explicit global config with optional company mappings
- event subscriptions
- jobs
- additive UI contributions

## Recommended Core Extension Surfaces

## 1. Platform module surfaces

These should stay close to the current registry style.

Candidates:

- `registerAgentAdapter()`
- `registerStorageProvider()`
- `registerSecretProvider()`
- `registerRunLogStore()`
- maybe `registerWorkspaceRuntime()` later

These are trusted platform modules, not casual plugins.

## 2. Connector plugin surfaces

These are the best near-term plugin candidates.

Capabilities:

- subscribe to domain events
- define scheduled sync jobs
- expose plugin-specific API routes under `/api/plugins/:pluginId/...`
- use company secret refs
- write plugin state
- publish dashboard data
- log activity through core APIs

Examples:

- Linear issue sync
- GitHub issue sync
- Grafana dashboard cards
- Stripe MRR / subscription rollups

## 3. Workspace-runtime surfaces

Your local-ops examples need first-party primitives plus plugin contributions.

Examples:

- file browser
- terminal
- git workflow
- child process tracking
- local dev server tracking

These should not be arbitrary third-party code directly poking the host filesystem and PTY layer through ad-hoc hooks.
Instead, Paperclip should add first-party services such as:

- project workspace service built on `project_workspaces`
- PTY session service
- process registry
- git service
- dev-server registry

Then plugins can add:

- UI panels on top of those services
- automations
- annotations
- external sync logic

This keeps sensitive local-machine behavior centralized and auditable.

## 4. UI contribution surfaces

Recommended first version:

- dashboard widgets
- settings panels
- detail-page tabs
- sidebar sections
- action buttons that invoke plugin routes

Recommended later version:

- richer remote UI modules

Recommended never or only with extreme caution:

- arbitrary override of core pages
- arbitrary replacement of routing/auth/layout logic

## Governance And Safety Requirements

Any Paperclip plugin system has to preserve core control-plane invariants from the repo docs.

That means:

- plugin install is global to the instance
- "companies" remain business objects in the API and data model, not tenant boundaries
- approval gates remain core-owned
- budget hard-stops remain core-owned
- mutating actions are activity-logged
- secrets remain ref-based and redacted in logs

I would require the following for every plugin:

## 1. Capability declaration

Every plugin declares a static capability set such as:

- `companies.read`
- `issues.read`
- `issues.write`
- `events.subscribe`
- `jobs.schedule`
- `http.outbound`
- `webhooks.receive`
- `assets.read`
- `assets.write`
- `workspace.pty`
- `workspace.fs.read`
- `workspace.fs.write`
- `secrets.read-ref`

The board/operator sees this before installation.

## 2. Global installation

A plugin is installed once and becomes available across the instance.
If it needs mappings over specific Paperclip objects, those are plugin data, not enable/disable boundaries.

## 3. Activity logging

Plugin-originated mutations should flow through the same activity log mechanism, with actor identity like:

- `actor_type = system`
- `actor_id = plugin:@paperclip/plugin-linear`

or a dedicated `plugin` actor type if you want stronger semantics later.

## 4. Health and failure reporting

Each plugin should expose:

- enabled/disabled state
- last successful run
- last error
- recent webhook/job history

One broken plugin must not break the rest of the company.

## 5. Secret handling

Plugins should receive secret refs, not raw secret values in config persistence.
Resolution should go through the existing secret provider abstraction.

## 6. Resource limits

Plugins should have:

- timeout limits
- concurrency limits
- retry policies
- optional per-plugin budgets

This matters especially for sync connectors and workspace plugins.

## Data Model Additions To Consider

I would avoid "arbitrary third-party plugin-defined SQL migrations" in the first version.
That is too much power too early.

The right mental model is:

- reuse core tables when the data is clearly part of Paperclip itself
- use generic extension tables for most plugin-owned state
- only allow plugin-specific tables later, and only for trusted platform modules or a tightly controlled migration workflow

## Recommended Postgres Strategy For Extensions

### 1. Core tables stay core

If a concept is becoming part of Paperclip's actual product model, it should get a normal first-party table.

Examples:

- `project_workspaces` is already a core table because project workspaces are now part of Paperclip itself
- if a future "project git state" becomes a core feature rather than plugin-owned metadata, that should also be a first-party table

### 2. Most plugins should start in generic extension tables

For most plugins, the host should provide a few generic persistence tables and the plugin stores namespaced records there.

This keeps the system manageable:

- simpler migrations
- simpler backup/restore
- simpler portability story
- easier operator review
- fewer chances for plugin schema drift to break the instance

### 3. Scope plugin data to Paperclip objects before adding custom schemas

A lot of plugin data naturally hangs off existing Paperclip objects:

- project workspace plugin state should often scope to `project` or `project_workspace`
- issue sync state should scope to `issue`
- metrics widgets may scope to `company`, `project`, or `goal`
- process tracking may scope to `project_workspace`, `agent`, or `run`

That gives a good default keying model before introducing custom tables.

### 4. Add trusted module migrations later, not arbitrary plugin migrations now

If Paperclip eventually needs extension-owned tables, I would only allow that for:

- trusted first-party packages
- trusted platform modules
- maybe explicitly installed admin-reviewed plugins with pinned versions

I would not let random third-party plugins run free-form schema migrations on startup.

Instead, add a controlled mechanism later if it becomes necessary.

## Suggested baseline extension tables

## 1. `plugins`

Instance-level installation record.

Suggested fields:

- `id`
- `package_name`
- `version`
- `kind`
- `manifest_json`
- `installed_at`
- `status`

## 2. `plugin_config`

Instance-level plugin config.

Suggested fields:

- `id`
- `plugin_id`
- `config_json`
- `installed_at`
- `updated_at`
- `last_error`

## 3. `plugin_state`

Generic key/value state for plugins.

Suggested fields:

- `id`
- `plugin_id`
- `scope_kind` (`instance | company | project | project_workspace | agent | issue | goal | run`)
- `scope_id` nullable
- `namespace`
- `state_key`
- `value_json`
- `updated_at`

This is enough for many connectors before allowing custom tables.

Examples:

- Linear external IDs keyed by `issue`
- GitHub sync cursors keyed by `project`
- file browser preferences keyed by `project_workspace`
- git branch metadata keyed by `project_workspace`
- process metadata keyed by `project_workspace` or `run`

## 4. `plugin_jobs`

Scheduled job and run tracking.

Suggested fields:

- `id`
- `plugin_id`
- `scope_kind` nullable
- `scope_id` nullable
- `job_key`
- `status`
- `last_started_at`
- `last_finished_at`
- `last_error`

## 5. `plugin_webhook_deliveries`

If plugins expose webhooks, delivery history is worth storing.

Suggested fields:

- `id`
- `plugin_id`
- `scope_kind` nullable
- `scope_id` nullable
- `endpoint_key`
- `status`
- `received_at`
- `response_code`
- `error`

## 6. Maybe later: `plugin_entities`

If generic plugin state becomes too limiting, add a structured, queryable entity table for connector records before allowing arbitrary plugin migrations.

Suggested fields:

- `id`
- `plugin_id`
- `entity_type`
- `scope_kind`
- `scope_id`
- `external_id`
- `title`
- `status`
- `data_json`
- `updated_at`

This is a useful middle ground:

- much more queryable than opaque key/value state
- still avoids letting every plugin create its own relational schema immediately

## How The Requested Examples Map To This Model

| Use case | Best fit | Core primitives needed | Notes |
|---|---|---|---|
| File browser | workspace plugin + schema UI | project workspaces, file API, audit rules | best anchored on project detail pages |
| Terminal | workspace plugin + PTY service | project workspaces, PTY/session service, process limits, audit events | should launch against a project workspace by default |
| Git workflow | workspace plugin | project workspaces, git service, repo/worktree model | project workspace is the natural repo anchor |
| Linear issue tracking | connector plugin | jobs, webhooks, secret refs, issue sync API | very strong plugin candidate |
| GitHub issue tracking | connector plugin | jobs, webhooks, secret refs | very strong plugin candidate |
| Grafana metrics | connector plugin + dashboard widget | outbound HTTP, widget API | probably read-only first |
| Child process/server tracking | workspace plugin | project workspaces, process registry, server heartbeat model | should attach processes to project workspaces when possible |
| Stripe revenue tracking | connector plugin | secret refs, scheduled sync, company metrics API | strong plugin candidate and aligns with future spec direction |

# Plugin Examples

## Workspace File Browser

Package idea: `@paperclip/plugin-workspace-files`

This plugin lets the board inspect project workspaces, agent workspaces, generated artifacts, and issue-related files without dropping to the shell. It is useful for:

- browsing files inside project workspaces
- debugging what an agent changed
- reviewing generated outputs before approval
- attaching files from a workspace to issues
- understanding repo layout for a company
- inspecting agent home workspaces in local-trusted mode

### UX

- Settings page: `/settings/plugins/workspace-files`
- Main page: `/:companyPrefix/plugins/workspace-files`
- Project tab: `/:companyPrefix/projects/:projectId?tab=files`
- Optional issue tab: `/:companyPrefix/issues/:issueId?tab=files`
- Optional agent tab: `/:companyPrefix/agents/:agentId?tab=workspace`

Main screens and interactions:

- Plugin settings:
  - choose whether the plugin defaults to `project.primaryWorkspace`
  - choose which project workspaces are visible
  - choose whether file writes are allowed or read-only
  - choose whether hidden files are visible
- Main explorer page:
  - project picker at the top
  - workspace picker scoped to the selected project's `workspaces`
  - tree view on the left
  - file preview pane on the right
  - search box for filename/path search
  - actions: copy path, download file, attach to issue, open diff
- Project tab:
  - opens directly into the project's primary workspace
  - lets the board switch among all project workspaces
  - shows workspace metadata like `cwd`, `repoUrl`, and `repoRef`
- Issue tab:
  - resolves the issue's project and opens that project's workspace context
  - shows files linked to the issue
  - lets the board pull files from the project workspace into issue attachments
  - shows the path and last modified info for each linked file
- Agent tab:
  - shows the agent's current resolved workspace
  - if the run is attached to a project, links back to the project workspace view
  - lets the board inspect files the agent is currently touching

Core workflows:

- Board opens a project and browses its primary workspace files.
- Board switches from one project workspace to another when a project has multiple checkouts or repo references.
- Board opens an issue, attaches a generated artifact from the file browser, and leaves a review comment.
- Board opens an agent detail page to inspect the exact files behind a failing run.

### Hooks needed

Recommended capabilities and extension points:

- `instance.settings.register`
- `ui.sidebar.register`
- `ui.page.register`
- `ui.detailTab.register` for `project`, `issue`, and `agent`
- `projects.read`
- `project.workspaces.read`
- `workspace.fs.read`
- optional `workspace.fs.write`
- `workspace.fs.stat`
- `workspace.fs.search`
- optional `assets.write`
- `activity.log.write`

Optional event subscriptions:

- `events.subscribe(agent.run.started)`
- `events.subscribe(agent.run.finished)`
- `events.subscribe(issue.attachment.created)`

Important constraint:

- the plugin should never read arbitrary host paths directly
- it should treat project workspaces as the canonical local file roots when a project is present
- it should only use first-party workspace/file APIs that enforce approved workspace roots

## Workspace Terminal

Package idea: `@paperclip/plugin-terminal`

This plugin gives the board a controlled terminal UI for project workspaces and agent workspaces. It is useful for:

- debugging stuck runs
- verifying environment state
- running targeted manual commands
- watching long-running commands
- pairing a human operator with an agent workflow

### UX

- Settings page: `/settings/plugins/terminal`
- Main page: `/:companyPrefix/plugins/terminal`
- Project tab: `/:companyPrefix/projects/:projectId?tab=terminal`
- Optional agent tab: `/:companyPrefix/agents/:agentId?tab=terminal`
- Optional run tab: `/:companyPrefix/agents/:agentId/runs/:runId?tab=terminal`

Main screens and interactions:

- Plugin settings:
  - allowed shells and shell policy
  - whether commands are read-only, free-form, or allow-listed
  - whether terminals require an explicit operator confirmation before launch
  - whether new terminal sessions default to the project's primary workspace
- Terminal home page:
  - list of active terminal sessions
  - button to open a new session
  - project picker, then workspace picker from that project's workspaces
  - optional agent association
  - terminal panel with input, resize, and reconnect support
  - controls: interrupt, kill, clear, save transcript
- Project terminal tab:
  - opens a session already scoped to the project's primary workspace
  - lets the board switch among the project's configured workspaces
  - shows recent commands and related process/server state for that project
- Agent terminal tab:
  - opens a session already scoped to the agent's workspace
  - shows recent related runs and commands
- Run terminal tab:
  - lets the board inspect the environment around a specific failed run

Core workflows:

- Board opens a terminal against an agent workspace to reproduce a failing command.
- Board opens a project page and launches a terminal directly in that project's primary workspace.
- Board watches a long-running dev server or test command from the terminal page.
- Board kills or interrupts a runaway process from the same UI.

### Hooks needed

Recommended capabilities and extension points:

- `instance.settings.register`
- `ui.sidebar.register`
- `ui.page.register`
- `ui.detailTab.register` for `project`, `agent`, and `run`
- `projects.read`
- `project.workspaces.read`
- `workspace.pty.open`
- `workspace.pty.input`
- `workspace.pty.resize`
- `workspace.pty.terminate`
- `workspace.pty.subscribe`
- `workspace.process.read`
- `activity.log.write`

Optional event subscriptions:

- `events.subscribe(agent.run.started)`
- `events.subscribe(agent.run.failed)`
- `events.subscribe(agent.run.cancelled)`

Important constraint:

- shell spawning must stay in a first-party PTY service
- the plugin should orchestrate and render sessions, not spawn raw host processes by itself

## Git Workflow

Package idea: `@paperclip/plugin-git`

This plugin adds repo-aware workflow tooling around issues and workspaces. It is useful for:

- branch creation tied to issues
- quick diff review
- commit and worktree visibility
- PR preparation
- treating the project's primary workspace as the canonical repo anchor
- seeing whether an agent's workspace is clean or dirty

### UX

- Settings page: `/settings/plugins/git`
- Main page: `/:companyPrefix/plugins/git`
- Project tab: `/:companyPrefix/projects/:projectId?tab=git`
- Optional issue tab: `/:companyPrefix/issues/:issueId?tab=git`
- Optional agent tab: `/:companyPrefix/agents/:agentId?tab=git`

Main screens and interactions:

- Plugin settings:
  - branch naming template
  - optional remote provider token secret ref
  - whether write actions are enabled or read-only
  - whether the plugin always uses `project.primaryWorkspace` unless a different project workspace is chosen
- Git overview page:
  - project picker and workspace picker
  - current branch
  - ahead/behind status
  - dirty files summary
  - recent commits
  - active worktrees
  - actions: refresh, create branch, create worktree, stage all, commit, open diff
- Project tab:
  - opens in the project's primary workspace
  - shows workspace metadata and repo binding (`cwd`, `repoUrl`, `repoRef`)
  - shows branch, diff, and commit history for that project workspace
- Issue tab:
  - resolves the issue's project and uses that project's workspace context
  - "create branch from issue" action
  - diff view scoped to the project's selected workspace
  - link branch/worktree metadata to the issue
- Agent tab:
  - shows the agent's branch, worktree, and dirty state
  - shows recent commits produced by that agent
  - if the agent is working inside a project workspace, links back to the project git tab

Core workflows:

- Board creates a branch from an issue and ties it to the project's primary workspace.
- Board opens a project page and reviews the diff for that project's workspace without leaving Paperclip.
- Board reviews the diff after a run without leaving Paperclip.
- Board opens a worktree list to understand parallel branches across agents.

### Hooks needed

Recommended capabilities and extension points:

- `instance.settings.register`
- `ui.sidebar.register`
- `ui.page.register`
- `ui.detailTab.register` for `project`, `issue`, and `agent`
- `ui.action.register`
- `projects.read`
- `project.workspaces.read`
- `workspace.git.status`
- `workspace.git.diff`
- `workspace.git.log`
- `workspace.git.branch.create`
- optional `workspace.git.commit`
- optional `workspace.git.worktree.create`
- optional `workspace.git.push`
- `activity.log.write`

Optional event subscriptions:

- `events.subscribe(issue.created)`
- `events.subscribe(issue.updated)`
- `events.subscribe(agent.run.finished)`

Important constraint:

- GitHub/GitLab PR creation should likely live in a separate connector plugin rather than overloading the local git plugin

## Linear Issue Tracking

Package idea: `@paperclip/plugin-linear`

This plugin syncs Paperclip work with Linear. It is useful for:

- importing backlog from Linear
- linking Paperclip issues to Linear issues
- syncing status, comments, and assignees
- mapping company goals/projects to external product planning
- giving board operators a single place to see sync health

### UX

- Settings page: `/settings/plugins/linear`
- Main page: `/:companyPrefix/plugins/linear`
- Dashboard widget: `/:companyPrefix/dashboard`
- Optional issue tab: `/:companyPrefix/issues/:issueId?tab=linear`
- Optional project tab: `/:companyPrefix/projects/:projectId?tab=linear`

Main screens and interactions:

- Plugin settings:
  - Linear API token secret ref
  - workspace/team/project mappings
  - status mapping between Paperclip and Linear
  - sync direction: import only, export only, bidirectional
  - comment sync toggle
- Linear overview page:
  - sync health card
  - recent sync jobs
  - mapped projects and teams
  - unresolved conflicts queue
  - import actions for teams, projects, and issues
- Issue tab:
  - linked Linear issue key and URL
  - sync status and last synced time
  - actions: link existing, create in Linear, resync now, unlink
  - timeline of synced comments/status changes
- Dashboard widget:
  - open sync errors
  - imported vs linked issues count
  - recent webhook/job failures

Core workflows:

- Board enables the plugin, maps a Linear team, and imports a backlog into Paperclip.
- Paperclip issue status changes push to Linear and Linear comments arrive back through webhooks.
- Board resolves mapping conflicts from the plugin page instead of silently drifting state.

### Hooks needed

Recommended capabilities and extension points:

- `instance.settings.register`
- `ui.sidebar.register`
- `ui.page.register`
- `ui.dashboardWidget.register`
- `ui.detailTab.register` for `issue` and `project`
- `events.subscribe(issue.created)`
- `events.subscribe(issue.updated)`
- `events.subscribe(issue.comment.created)`
- `events.subscribe(project.updated)`
- `jobs.schedule`
- `webhooks.receive`
- `http.outbound`
- `secrets.read-ref`
- `plugin.state.read`
- `plugin.state.write`
- optional `issues.create`
- optional `issues.update`
- optional `issue.comments.create`
- `activity.log.write`

Important constraint:

- webhook processing should be idempotent and conflict-aware
- external IDs and sync cursors belong in plugin-owned state, not inline on core issue rows in the first version

## GitHub Issue Tracking

Package idea: `@paperclip/plugin-github-issues`

This plugin syncs Paperclip issues with GitHub Issues and optionally links PRs. It is useful for:

- importing repo backlogs
- mirroring issue status and comments
- linking PRs to Paperclip issues
- tracking cross-repo work from inside one company view
- bridging engineering workflow with Paperclip task governance

### UX

- Settings page: `/settings/plugins/github-issues`
- Main page: `/:companyPrefix/plugins/github-issues`
- Dashboard widget: `/:companyPrefix/dashboard`
- Optional issue tab: `/:companyPrefix/issues/:issueId?tab=github`
- Optional project tab: `/:companyPrefix/projects/:projectId?tab=github`

Main screens and interactions:

- Plugin settings:
  - GitHub App or PAT secret ref
  - org/repo mappings
  - label/status mapping
  - whether PR linking is enabled
  - whether new Paperclip issues should create GitHub issues automatically
- GitHub overview page:
  - repo mapping list
  - sync health and recent webhook events
  - import backlog action
  - queue of unlinked GitHub issues
- Issue tab:
  - linked GitHub issue and optional linked PRs
  - actions: create GitHub issue, link existing issue, unlink, resync
  - comment/status sync timeline
- Dashboard widget:
  - open PRs linked to active Paperclip issues
  - webhook failures
  - sync lag metrics

Core workflows:

- Board imports GitHub Issues for a repo into Paperclip.
- GitHub webhooks update status/comment state in Paperclip.
- A PR is linked back to the Paperclip issue so the board can follow delivery status.

### Hooks needed

Recommended capabilities and extension points:

- `instance.settings.register`
- `ui.sidebar.register`
- `ui.page.register`
- `ui.dashboardWidget.register`
- `ui.detailTab.register` for `issue` and `project`
- `events.subscribe(issue.created)`
- `events.subscribe(issue.updated)`
- `events.subscribe(issue.comment.created)`
- `jobs.schedule`
- `webhooks.receive`
- `http.outbound`
- `secrets.read-ref`
- `plugin.state.read`
- `plugin.state.write`
- optional `issues.create`
- optional `issues.update`
- optional `issue.comments.create`
- `activity.log.write`

Important constraint:

- keep "local git state" and "remote GitHub issue state" in separate plugins even if they work together

## Grafana Metrics

Package idea: `@paperclip/plugin-grafana`

This plugin surfaces external metrics and dashboards inside Paperclip. It is useful for:

- company KPI visibility
- infrastructure/incident monitoring
- showing deploy, traffic, latency, or revenue charts next to work
- creating Paperclip issues from anomalous metrics

### UX

- Settings page: `/settings/plugins/grafana`
- Main page: `/:companyPrefix/plugins/grafana`
- Dashboard widgets: `/:companyPrefix/dashboard`
- Optional goal tab: `/:companyPrefix/goals/:goalId?tab=metrics`

Main screens and interactions:

- Plugin settings:
  - Grafana base URL
  - service account token secret ref
  - dashboard and panel mappings
  - refresh interval
  - optional alert threshold rules
- Dashboard widgets:
  - one or more metric cards on the main dashboard
  - quick trend view and last refresh time
  - link out to Grafana and link in to the full Paperclip plugin page
- Full metrics page:
  - selected dashboard panels embedded or proxied
  - metric selector
  - time range selector
  - "create issue from anomaly" action
- Goal tab:
  - metric cards relevant to a specific goal or project

Core workflows:

- Board sees service degradation or business KPI movement directly on the Paperclip dashboard.
- Board clicks into the full metrics page to inspect the relevant Grafana panels.
- Board creates a Paperclip issue from a threshold breach with a metric snapshot attached.

### Hooks needed

Recommended capabilities and extension points:

- `instance.settings.register`
- `ui.dashboardWidget.register`
- `ui.page.register`
- `ui.detailTab.register` for `goal` or `project`
- `jobs.schedule`
- `http.outbound`
- `secrets.read-ref`
- `plugin.state.read`
- `plugin.state.write`
- optional `issues.create`
- optional `assets.write`
- `activity.log.write`

Optional event subscriptions:

- `events.subscribe(goal.created)`
- `events.subscribe(project.updated)`

Important constraint:

- start read-only first
- do not make Grafana alerting logic part of Paperclip core; keep it as additive signal and issue creation

## Child Process / Server Tracking

Package idea: `@paperclip/plugin-runtime-processes`

This plugin tracks long-lived local processes and dev servers started in project workspaces. It is useful for:

- seeing which agent started which local service
- tracking ports, health, and uptime
- restarting failed dev servers
- exposing process state alongside issue and run state
- making local development workflows visible to the board

### UX

- Settings page: `/settings/plugins/runtime-processes`
- Main page: `/:companyPrefix/plugins/runtime-processes`
- Dashboard widget: `/:companyPrefix/dashboard`
- Process detail page: `/:companyPrefix/plugins/runtime-processes/:processId`
- Project tab: `/:companyPrefix/projects/:projectId?tab=processes`
- Optional agent tab: `/:companyPrefix/agents/:agentId?tab=processes`

Main screens and interactions:

- Plugin settings:
  - whether manual process registration is allowed
  - health check behavior
  - whether operators can stop/restart processes
  - log retention preferences
- Process list page:
  - status table with name, command, cwd, owner agent, port, uptime, and health
  - filters for running/exited/crashed processes
  - actions: inspect, stop, restart, tail logs
- Project tab:
  - filters the process list to the project's workspaces
  - shows which workspace each process belongs to
  - groups processes by project workspace
- Process detail page:
  - process metadata
  - live log tail
  - health check history
  - links to associated issue or run
- Agent tab:
  - shows processes started by or assigned to that agent

Core workflows:

- An agent starts a dev server; the first-party process service registers it and the plugin renders it.
- Board opens a project and immediately sees the processes attached to that project's workspace.
- Board sees a crashed process on the dashboard and restarts it from the plugin page.
- Board attaches process logs to an issue when debugging a failure.

### Hooks needed

Recommended capabilities and extension points:

- `instance.settings.register`
- `ui.sidebar.register`
- `ui.page.register`
- `ui.dashboardWidget.register`
- `ui.detailTab.register` for `project` and `agent`
- `projects.read`
- `project.workspaces.read`
- `workspace.process.register`
- `workspace.process.list`
- `workspace.process.read`
- optional `workspace.process.terminate`
- optional `workspace.process.restart`
- `workspace.process.logs.read`
- optional `workspace.http.probe`
- `plugin.state.read`
- `plugin.state.write`
- `activity.log.write`

Optional event subscriptions:

- `events.subscribe(agent.run.started)`
- `events.subscribe(agent.run.finished)`
- `events.subscribe(process.started)`
- `events.subscribe(process.exited)`

Important constraint:

- this should be built on a first-party process registry
- the plugin should not own raw child-process spawning on its own

## Stripe Revenue Tracking

Package idea: `@paperclip/plugin-stripe`

This plugin pulls Stripe revenue and subscription data into Paperclip. It is useful for:

- showing MRR and churn next to company goals
- tracking trials, conversions, and failed payments
- letting the board connect revenue movement to ongoing work
- enabling future financial dashboards beyond token costs

### UX

- Settings page: `/settings/plugins/stripe`
- Main page: `/:companyPrefix/plugins/stripe`
- Dashboard widgets: `/:companyPrefix/dashboard`
- Optional company/goal metric tabs if those surfaces exist later

Main screens and interactions:

- Plugin settings:
  - Stripe secret key secret ref
  - account selection if needed
  - metric definitions such as MRR treatment and trial handling
  - sync interval
  - webhook signing secret ref
- Dashboard widgets:
  - MRR card
  - active subscriptions
  - trial-to-paid conversion
  - failed payment alerts
- Stripe overview page:
  - time series charts
  - recent customer/subscription events
  - webhook health
  - sync history
  - action: create issue from billing anomaly

Core workflows:

- Board enables the plugin and connects a Stripe account.
- Webhooks and scheduled reconciliation keep plugin state current.
- Revenue widgets appear on the main dashboard and can be linked to company goals.
- Failed payment spikes or churn events can generate Paperclip issues for follow-up.

### Hooks needed

Recommended capabilities and extension points:

- `instance.settings.register`
- `ui.dashboardWidget.register`
- `ui.page.register`
- `jobs.schedule`
- `webhooks.receive`
- `http.outbound`
- `secrets.read-ref`
- `plugin.state.read`
- `plugin.state.write`
- `company.metrics.write`
- optional `issues.create`
- `activity.log.write`

Important constraint:

- Stripe data should stay additive to Paperclip core
- it should not leak into core budgeting logic, which is specifically about model/token spend in V1

## Specific Patterns From OpenCode Worth Adopting

## Adopt

- separate SDK package from runtime loader
- deterministic load order and precedence
- very small authoring API
- typed schemas for plugin inputs/config/tools
- internal extensions using the same registration shapes as external ones when reasonable
- plugin load errors isolated from host startup when possible
- explicit community-facing plugin docs and example templates

## Adapt, not copy

- local path loading
- dependency auto-install
- hook mutation model
- built-in override behavior
- broad runtime context objects

## Avoid

- project-local arbitrary code loading
- implicit trust of npm packages at startup
- plugins overriding core invariants
- unsandboxed in-process execution as the default extension model

## Suggested Rollout Plan

## Phase 0: Harden the seams that already exist

- formalize adapter/storage/secret/run-log registries as "platform modules"
- remove ad-hoc fallback behavior where possible
- document stable registration contracts

## Phase 1: Add connector plugins first

This is the highest-value, lowest-risk plugin category.

Build:

- plugin manifest
- global install/update lifecycle
- global plugin config and optional company-mapping storage
- secret ref access
- typed domain event subscription
- scheduled jobs
- webhook endpoints
- activity logging helpers
- dashboard widget and settings-panel contributions

This phase would immediately cover:

- Linear
- GitHub
- Grafana
- Stripe

## Phase 2: Add workspace services and workspace plugins

Build first-party primitives:

- project workspace service built on `project_workspaces`
- PTY/session service
- file service
- git service
- process/service tracker

Then expose additive plugin/UI surfaces on top.

This phase covers:

- file browser
- terminal
- git workflow
- child process/server tracking

## Phase 3: Consider richer UI and plugin packaging

Only after phases 1 and 2 are stable:

- richer frontend extension support
- signed/verified plugin packages
- plugin marketplace
- optional custom plugin storage backends or migrations

## Recommended Architecture Decision

If I had to collapse this report into one architectural decision, it would be:

Paperclip should not implement "an OpenCode-style generic in-process hook system."
Paperclip should implement "a plugin platform with multiple trust tiers":

- trusted platform modules for low-level runtime integration
- typed out-of-process plugins for instance-wide integrations and automation
- schema-driven UI contributions
- core-owned invariants that plugins can observe and act around, but not replace

That gets the upside of `opencode`'s extensibility without importing the wrong threat model.

## Concrete Next Steps I Would Take In Paperclip

1. Write a short extension architecture RFC that formalizes the distinction between `platform modules` and `plugins`.
2. Introduce a small plugin manifest type in `packages/shared` and a `plugins` install/config section in the instance config.
3. Build a typed domain event bus around existing activity/live-event patterns, but keep core invariants non-hookable.
4. Implement connector-plugin MVP only: global install/config, secret refs, jobs, webhooks, settings panel, dashboard widget.
5. Treat workspace features as a separate track that starts by building core workspace primitives, not raw plugin hooks.
