# 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.