Plugins
Custom Plugin
Build a custom plugin with config, actions, hooks, system text, availability checks, lifecycle, and optional runtime HTTP routes
Custom Plugin
If the thing you want to add is mainly:
- a set of actions
- a set of hooks
- one system text layer
- one lifecycle-owned runtime boundary
- one set of runtime HTTP routes
then a custom plugin is usually the right extension point.
Current public entry points
@downcity/agent exposes:
- plugin types and contracts
- the
BasePluginbase class - the local
Agentplugins: [...]assembly entry point
Built-in plugin classes are exported from @downcity/plugins.
Minimal skeleton
import { BasePlugin, type PluginActions } from "@downcity/agent";
export class NotesPlugin extends BasePlugin {
readonly name = "notes";
readonly title = "Notes Helper";
readonly description = "Adds note-related actions and prompt guidance.";
readonly actions: PluginActions = {
status: {
execute: async ({ context }) => {
return {
success: true,
data: {
rootPath: context.rootPath,
},
};
},
},
};
}Attach the custom plugin to a local Agent
import { Agent, BasePlugin, type PluginActions } from "@downcity/agent";
class NotesPlugin extends BasePlugin {
readonly name = "notes";
readonly title = "Notes Helper";
readonly description = "Adds note-related actions and runtime guidance.";
readonly actions: PluginActions = {
status: {
allowWhenDisabled: true,
execute: async ({ context }) => ({
success: true,
data: {
rootPath: context.rootPath,
},
}),
},
};
}
const agent = new Agent({
id: "repo-helper",
path: "/path/to/project",
tools: {},
plugins: [new NotesPlugin()],
});The most important design questions
1. What exactly are you extending
Be explicit about whether you need:
- actions
- hooks
- resolve points
- system text
- lifecycle
- HTTP
Do not default to implementing every field.
2. Should some actions run while disabled
Actions like:
statusinstallconfigure
often need allowWhenDisabled: true.
3. Should runtime state stay inside lifecycle
If the capability must:
- start and stop with the plugin
- keep runtime state in memory
- recover after restart
then model that inside plugin lifecycle instead of hiding it in unrelated hooks.
One practical rule
Your plugin logic should rely on the minimal stable plugin context when possible instead of assuming access to every runtime singleton.