* docs: add plugin system guide, authoring tutorial, and manifest reference
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: update plugin docs for host data API, config, and nav additions
Cherry-picked plugin docs (d853b66b8) were written before four follow-up
PRs merged: typed capability-gated Host data API (#1753, ADR 0043),
richer frontend SDK (#1759), per-plugin settings pages (#1761), and
sidebar Integrations nav items (#1767). Re-verify against the merged
code and bring the docs current:
- plugins.md: per-plugin settings/config page, expanded Host RPC list,
updated capability vocabulary (api_read/api_write), sidebar nav note.
- plugins-authoring.md: GetConfig/GetSecret/SetSecret/DeleteSecret and
the six Host data readers, registerRoute options/PluginPageChrome,
NavItem icon/integrations section, host.api.baseUrl/navigate, and the
full host.ui component list.
- plugins-manifest.md: corrected api_read/api_write semantics (now
enforced, not reserved), config_schema validation/secret-masking
behavior, and new Host data API / config-schema sections.
- configuration.md, feature-status.md: document the KANDEV_FEATURES_PLUGINS
flag alongside the existing Office flag.
- coverage.json: new plugin-system coverage area so the public-docs
validator accounts for the three new pages.
* docs: fix plugin config route, signing claim, and data-dir layout
Address automated review findings on PR #1774:
- plugins-manifest.md: PATCH route for config is /api/plugins/{id}, not
/api/plugins/{id}/config (only GET uses the /config suffix).
- plugins-authoring.md: host.store is not read-only — setState is
genuinely exposed and writable; correct the misleading inline comment.
- plugins.md: signature verification is not currently wired up in
production (pkgtar.VerifySignature is nil, never assigned outside
tests), so every package installs and is reported as unsigned today —
correct the 'kandev verifies it' claim in both the install pipeline
step and the signed/unsigned section.
- plugins.md: KANDEV_PLUGIN_DATA_DIR is <id>/data, a sibling of the
<version>/ directory (shared across versions), not nested inside it —
fix the on-disk layout diagram.
* docs: mark plugin docs as experimental
The plugin system is feature-flagged and off by default in production
(KANDEV_FEATURES_PLUGINS). Mark all three plugin pages with
status: experimental frontmatter, matching the k8s.md convention for a
page that is wholly about an experimental feature.
* docs: add plugin UI screenshots and a capture spec
Embed four operator-facing screenshots in the plugin docs (the Plugins
list, the install dialog, a per-plugin settings page with its manifest
card, and a native plugin route). Captured from an isolated e2e backend
with the kandev-plugin-hello reference example installed.
Add e2e/tests/plugins/plugins-docs-screenshots.spec.ts to regenerate them:
it reuses the plugins e2e harness (isolated backend, plugins flag on) and
is skipped unless CAPTURE_DOCS_MEDIA=1, so it adds no CI cost. Defaults to
the repo's own e2e fixture package; override DOCS_PLUGIN_* to point at the
polished hello example (as the committed captures were taken).
* docs: fix plugin screenshot paths for the landing publisher
The Cloudflare preview build failed because markdown images under
docs/public/media/ compile to build-time module imports that the docs
site can't resolve — media/ is reserved for <DocsVideo> assets. The
landing publisher only rewrites and copies images referenced under
screenshots/ (README convention). Move the four plugin captures to
docs/screenshots/plugin-*.png and reference them as ../screenshots/*.png,
and update the capture spec to write there.
* docs: convert plugin how-it-works diagram to mermaid
The hand-drawn ASCII pipeline rendered with broken alignment and stray
syntax highlighting on the docs site. Replace it with a mermaid flowchart
(same convention as architecture.md), which renders as a clean SVG.
* docs: give plugins its own nav section
* docs: clarify plugin tools are declared/listed, not agent-invocable yet
Plugin tools[] are validated and exposed via GET /api/plugins/tools, and
the gRPC InvokeTool transport is implemented and tested, but nothing in
production calls it — no agent, MCP bridge, or HTTP route. The docs
overstated this as agent-invocable. Correct the Plugin interface comment,
the manifest tools[] rows, the how-it-works diagram, and the example
plugin description, and add a note in both the authoring and manifest
pages that InvokeTool stays unused until the agent-tool wiring ships.
* docs: clarify webhook relay in how-it-works diagram
The diagram collapsed two hops into one node, reading as if the plugin
receives the external HTTP POST directly. Split it: an external caller
POSTs to kandev's webhook route, which relays the request to the plugin
over the gRPC HandleWebhook method (the plugin serves no HTTP itself).
Also label the DeliverEvent/InvokeTool/HandleWebhook edges by their real
proto method names.
* docs: note the plugin backend is any go-plugin gRPC executable, Go SDK only
* docs: remove plugin tools from the docs
Plugin tools[] overlap with MCP (kandev's established agent-tool surface)
and the invocation path was never wired to agents. Rather than document a
half-feature that implies plugins are an alternate MCP, drop it entirely:
the Plugin interface (InvokeTool), the manifest tools[] block and field
rows, the how-it-works diagram branch, and the example-plugin mention.
Removal from the backend code is tracked separately.
* docs: replace how-it-works mermaid with a designed diagram image
The mermaid flowchart rendered poorly. Replace it with a hand-authored
HTML/CSS architecture figure (install pipeline + the runtime gRPC
interactions), rendered to docs/screenshots/plugin-architecture.png. The
diagram source lives in the docs-screenshot capture spec (skipped unless
CAPTURE_DOCS_MEDIA=1) so it regenerates deterministically at 2x.
* docs: make the how-it-works figure a real box-and-arrow diagram
The previous figure read as a list of rows. Replace it with a hand-drawn
SVG architecture diagram: the install pipeline feeds a spawned plugin
subprocess, then kandev and the plugin communicate over one gRPC
connection (DeliverEvent, HandleWebhook, and the plugin's Host API
callbacks), with the external webhook caller and the browser SPA as
edge nodes. Source lives in the capture spec's ARCH_HTML.
* docs: make the how-it-works diagram a clean monochrome flowchart
Drop the multi-color component diagram for a calm top-down flowchart
(mermaid's shape, drawn properly): install → verify → extract → spawn,
branching into event delivery and webhook relay, converging on the Host
API callbacks, with an optional native-UI step. Grayscale, no accent
colors. Source stays in the capture spec's ARCH_HTML.
* docs: dark plugin diagram + space out the two page images
- Make the how-it-works flowchart a dark rounded card (matches the docs
page in dark mode; reads as an embedded card in light). Rendered with
omitBackground so the corners are transparent.
- Move the Settings > Plugins list screenshot out of the intro (where it
sat right before the diagram) down into the "Installing a plugin"
section, so text separates the two images.
* docs: update install-dialog screenshot for aligned footer actions
Recaptured after fix(plugins): align install-dialog footer actions
(#1780) — Cancel and Install now sit together in one right-aligned
footer row instead of the Install button being stranded under the
dropzone.
* docs: account for /settings/system/storage in coverage.json
The storage settings route (added by #1699) was never recorded in the
public-docs coverage inventory — the docs validator is path-filtered to
docs/public/** so it didn't run for that PR. This docs PR is the first to
trip it. Add the route to system-health-and-maintenance.
* docs: review fixes — remove stale tools refs, tighten accuracy
Review pass against the now-merged main (tools removed in #1776):
- extending-kandev.md: drop "serves agent-invocable tools" from the plugin
overview (the feature no longer exists).
- plugins-manifest.md: the legacy endpoints block is health/events/webhooks
only (no tools path); and the `signed` field is always false today since
signature verification isn't wired.
- plugins.md: install validation covers the categories/UI-surface enums (not
a "capability vocabulary"); AutoMTLS secures the channel on all platforms,
not just Windows; and status vs signing are separate badges.
* docs: explain what Host state is (GetState/SetState)
The authoring guide listed the state RPCs but never said what 'state'
means. Add a plain-language explanation: a per-plugin, kandev-persisted
key/value store keyed by (scope, scopeID, key) holding JSON objects,
namespaced per plugin, scoped to instance/workspace/task/agent, and how it
differs from the writable data dir. Make the example a real increment.
* docs: note NavItem section:'settings' is not rendered
A NavItem with section:'settings' is accepted by the type but no sidebar
section renders it (plugin-nav-items renders main/default, the Integrations
sections render 'integrations'); annotate that settings pages use
registerSettingsRoute. Addresses repeated review feedback.
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
8.6 KiB
title, description
| title | description |
|---|---|
| Extending Kandev | Add agents, executors, integrations, workflow behavior, MCP tools, plugins, settings, or workbench surfaces at their real ownership boundaries. |
Extending Kandev
An extension is complete when discovery/configuration, durable state, runtime behavior, recovery, security, tests, packaging, and public documentation agree. Registration and UI presence alone are not completion.
Add an agent
For a local passthrough-only CLI, users can choose Settings → Agents → Add TUI Agent. That path persists a custom definition and default profile; no source patch is required.
To ship a built-in integration, add an agents.Agent implementation under apps/backend/internal/agent/agents/ and register it in internal/agent/registry/registry.go. Structured agents currently need an ACP-speaking process. Optional interfaces add inference, CLI passthrough, native-binary preference, or interactive login.
Follow Add an Agent CLI. Cover stable identity, installation detection, command construction, permissions, authentication, model/mode probing, resume, MCP delivery, remote runtime behavior, assets, registry tests, and packaging when a binary is bundled.
Add an executor
Executors span product configuration and runtime implementation:
internal/task/models/models.godefines persistedExecutorType,Executor, andExecutorProfile;internal/agent/executor/keeps product-to-runtime mappings typed;internal/agent/runtime/lifecycle/executor_backend.godefines the runtime backend;internal/agent/runtime/lifecycle/env_preparer.goseparates environment preparation;internal/backendapp/agents.goregisters backends and preparers;- task repository defaults/handlers and web settings expose built-in profiles.
Local and worktree use the same standalone process backend but different materialization. Docker, remote Docker, SSH, and Sprites own distinct create/connect/cleanup behavior.
A production executor needs profile persistence and validation, scoped credentials, create/prepare/start/observe/stop/cleanup, agentctl delivery and resolution, multi-repository materialization, durable runtime IDs/status/errors, startup recovery, settings UI, and lifecycle/failure tests. Cleanup must be idempotent and must not delete another task's host, container, directory, or credential.
Do not advertise an executor until restart, cancellation, partial-create cleanup, connectivity, supported capabilities, cost, and credential boundaries are documented.
Add a provider integration
Choose the closest existing provider domain under apps/backend/internal/. There is no universal configuration shape:
- Jira and Linear use workspace-scoped connection state;
- Sentry supports multiple named workspace instances;
- GitHub and GitLab split some global authentication/host state from workspace watches or presets;
- Slack follows a workspace-oriented pattern.
A domain may contain a client, store/repository, service, provider, handlers, and poller. Shared helpers under internal/integrations/ cover secret adapters, health polling, and workspace scope. Construction and non-fatal provider startup live in internal/backendapp/. Web clients/hooks live in lib/api/domains/ and hooks/domains/; settings and shared provider UI live under app/settings/ and components/integrations/.
Preserve the chosen global/workspace/instance scope through storage, secrets, routes, and UI. Validate custom hosts against SSRF, redact tokens and provider errors, handle pagination/rate limits, deduplicate polls/webhooks, expose health, and treat external titles/descriptions/comments as untrusted content. Add provider fakes and user-journey coverage.
Add workflow or automation behavior
internal/workflow/ owns workflow models, repository, service, engine, adapters, and transitions. internal/automation/ owns workspace rules that can create or react to work. Preserve the distinction between a task transition and an automation trigger.
New actions or events need durable semantics, import/export compatibility when applicable, retry and duplicate handling, cycle prevention, UI editing, and tests using old saved workflow definitions.
Add an MCP tool
internal/mcp/server/server.go owns tool schemas and availability by task, config, external, or office mode. Backend behavior lives in internal/mcp/handlers/; registration and dependencies are wired from internal/backendapp/. Agentctl hosts MCP transports and relays calls over the agent stream.
A new relayed tool normally requires:
- a schema, tool name, and mode assignment in the server;
- a WebSocket action in
apps/backend/pkg/websocket/actions.go; - a handler and registration in
internal/mcp/handlers/; - server mode/count, handler, transport, and integration tests;
- an update to Automation and MCP when capability changes.
Inject task/session identity from server context instead of trusting arguments. Enforce task/workspace reachability, confirmation for destructive actions, pagination, concurrency behavior, and least-privilege credentials. The backend's external MCP routes currently have no Kandev user-auth middleware; deployment network controls are part of the security boundary.
Build a plugin
Plugins are a peer extension mechanism to the seams above, aimed at
extensions that should ship and version independently of a kandev release.
A plugin backend is a Go binary that kandev spawns and supervises as a
subprocess, communicating over a strict typed gRPC protocol
(internal/plugins/, pkg/pluginsdk) — it receives bus events and relays
external webhooks, calling back into kandev through a capability-gated Host
RPC service (state, secrets, read-only data, cross-plugin events). A plugin may additionally ship an optional native
frontend bundle that the SPA loads at boot to register real routes, nav
items, slot components, and WebSocket handlers, sharing kandev's own React
instance and app store.
Plugins are distributed as a signed-or-unsigned tarball and installed by URL,
manual upload, or filesystem sideload/sync — there is no manifest-paste
registration step and no credentials to issue. The whole system sits behind
the plugins feature flag (Settings > System > Feature Toggles), off by
default in production. See Plugins for the operator-facing
install/operate flow, Authoring a plugin for the
build tutorial and SDK reference, and the Plugin manifest
reference for the complete manifest.yaml schema.
Add settings, flags, or workbench UI
Create backend ownership and validation before a web form. Web settings require the appropriate domain client/hook, src/settings-routes.tsx, page/components, and sidebar or general navigation. Add a global state slice only when multiple surfaces or WebSocket hydration require it.
Wire runtime feature flags through every enforcement layer in one change. Add the default to profiles.yaml and FeaturesConfig, register its environment variable and runtime behavior in internal/runtimeflags/registry.go, and gate backend construction, handlers, or other call sites. Mirror the field in the frontend feature types, use useFeature for client surfaces, and call notFound() from the server layout or page when a guessed URL must remain unavailable. Add backend and frontend tests for both states. A flag is incomplete if any layer can expose or execute the feature while it is disabled. Do not turn an internal package or environment variable into a public setting without first defining its compatibility contract and support status.
Workbench changes must preserve task/session/repository selection, dock-layout restoration, reconnect behavior, and the separate mobile layout. Test unavailable dependencies, invalid credentials, save/test failures, keyboard access, and narrow viewports.
Completion checklist
- Startup/registry wiring cannot silently omit the extension.
- Persisted types, defaults, migrations, APIs, and UI use one stable identity.
- Config and secrets have explicit global/workspace/task/instance scope and redaction.
- Optional dependencies produce truthful capability and health state.
- Retry, recovery, cancellation, and idempotent cleanup are defined.
- Cross-process and wire compatibility is tested.
- Unit, integration, and required Playwright coverage pass.
- Release bundles contain every required binary, asset, image, and platform mapping.
- Public docs cover setup, status, trust boundary, limits, and troubleshooting.
Related: Architecture, Backend development, Web development, and Testing.