Extending the Build Pipeline
AgentPlugins ships seven built-in adapters. If you maintain an internal harness, want to add custom lint rules, or need to transform the manifest IR before code generation, the plugins field in defineConfig gives you full access to the build pipeline without forking anything.
When to use plugins
| Need | Mechanism |
|---|---|
| Compile to a private/internal harness | plugin.adapter |
| Add project-specific lint checks | plugin.lintRules |
| Add a new emit language | plugin.emitters |
| Validate or reject the manifest before build | plugin.preValidate middleware |
| Mutate the manifest IR (e.g. inject metadata) | plugin.transformIR middleware |
| Inspect or rewrite emitted files per-target | plugin.postEmit middleware |
| Gate or audit install steps | plugin.onInstall / plugin.onAudit middleware |
Basic setup
// agentplugins.config.ts
import { defineConfig } from '@agentplugins/core'
export default defineConfig({
manifest: {
name: 'my-plugin',
version: '1.0.0',
description: 'My cross-platform plugin',
hooks: { /* ... */ },
},
plugins: [
{
name: 'my-extension',
// fields below — mix and match
},
],
})All plugins entries are composed on top of the built-in adapter set. Built-in adapters (claude, codex, …) are always registered first; your plugins run after and may override them by registering the same target name.
Custom adapter
A PlatformAdapter tells the build system how to validate and compile the manifest into your harness's native format.
// src/my-harness-adapter.ts
import type { PlatformAdapter } from '@agentplugins/core'
export const myHarnessAdapter: PlatformAdapter = {
name: 'my-harness', // target id — must match the targets[] list
displayName: 'My Harness',
supportedHooks: ['sessionStart', 'preToolUse', 'postToolUse'],
supportedHandlers: ['command', 'inline'],
manifestPath: 'my-harness.json',
manifestFormat: 'json',
validate(plugin) {
// Return ValidationIssue[] — errors abort build, warnings are printed
return []
},
compile(plugin) {
return {
files: [
{
path: 'my-harness.json',
content: JSON.stringify({ name: plugin.name, version: plugin.version }, null, 2),
},
],
manifest: {},
warnings: [],
issues: [],
}
},
}Wire it in via defineConfig:
import { defineConfig } from '@agentplugins/core'
import { myHarnessAdapter } from './src/my-harness-adapter.js'
export default defineConfig({
manifest: { name: 'my-plugin', version: '1.0.0', description: '…' },
plugins: [
{ name: 'my-harness-adapter', adapter: myHarnessAdapter },
],
targets: ['claude', 'my-harness'],
})my-harness is now a valid target id. Unknown target ids that have no registered adapter are skipped at build time with a warning.
Full working example
plugins/example-custom-adapter/ in the repository shows this pattern end-to-end, producing dist/claude/ and dist/my-harness/ from one agentplugins build run.
Custom lint rules
Add build-time checks that run alongside the built-in lint rules:
import type { LintRule } from '@agentplugins/pipeline'
const requireLicenseRule: LintRule = {
id: 'require-license',
description: 'All plugins in this org must declare a license',
run(ctx) {
if (!ctx.manifest.license) {
return [{
severity: 'error',
field: 'license',
message: 'license is required for org plugins',
suggestion: 'Add license: "MIT" to your manifest',
}]
}
return []
},
}
export default defineConfig({
manifest: { /* … */ },
plugins: [
{ name: 'org-rules', lintRules: [requireLicenseRule] },
],
})Custom rules run in strict mode by default — errors abort the build, warnings are printed.
Pipeline middleware
Middleware functions follow the standard (ctx, next) => Promise<void> onion pattern. Call await next() to proceed, or ctx.abort(reason) to stop the pipeline.
preValidate — reject before validation
Runs before validateUniversal(). Use it to enforce org-wide manifest constraints:
{
name: 'org-guard',
preValidate: async (ctx, next) => {
if (!ctx.manifest.name.startsWith('acme-')) {
ctx.abort('All ACME plugins must be named acme-*')
}
await next()
},
}transformIR — mutate the manifest IR
Runs after validation, before code generation. Use it to inject metadata or normalize fields:
{
name: 'inject-build-metadata',
transformIR: async (ctx, next) => {
ctx.manifest = {
...ctx.manifest,
description: `[${process.env.CI_COMMIT_SHA?.slice(0, 7) ?? 'local'}] ${ctx.manifest.description}`,
}
await next()
},
}postEmit — inspect or rewrite emitted files
Runs per-target after the adapter has produced its files. ctx.files is the mutable list of { path, content } entries:
{
name: 'add-banner',
postEmit: async (ctx, next) => {
ctx.files = ctx.files.map(f => ({
...f,
content: `// Built by ACME CI — do not edit\n${f.content}`,
}))
await next()
},
}onInstall — gate or audit install
Runs during agentplugins add. The built-in security checks run here — pinned integrity hash verification, then script policy evaluation. You may add your own checks after them:
{
name: 'org-install-policy',
onInstall: async (ctx, next) => {
if (ctx.pluginName.startsWith('untrusted-')) {
ctx.abort(`Plugin "${ctx.pluginName}" is blocked by org policy`)
}
await next()
},
}WARNING
onInstall plugins run in the user's environment, not the plugin author's. Only ship install middleware as part of org-internal tooling, not in public plugins.
Plugin interface reference
interface Plugin {
readonly name: string
// Compile
adapter?: PlatformAdapter
lintRules?: LintRule[]
emitters?: Record<string, CodeEmitter>
// Build pipeline middleware
preValidate?: Middleware<BuildCtx>
transformIR?: Middleware<BuildCtx>
postEmit?: Middleware<TargetCtx>
// Install pipeline middleware
onAudit?: Middleware<InstallCtx>
onInstall?: Middleware<InstallCtx>
}Each field is optional — a plugin may contribute any combination.
Middleware execution order
validateUniversal()— structural checks on the manifestlint()— with merged lint rules from all plugins- All
preValidatechains run — can abort before compilation - All
transformIRchains run — may mutatectx.manifest - Per-target:
validateForPlatform()→adapter.compile()→ allpostEmitchains - Files written to
dist/
Install pipeline:
- All
onInstallchains run (pinned-integrity and script-policy checks first) - Files linked into agent directories
See also
- Creating Plugins — manifest authoring from scratch
- Adapters reference — built-in adapter output formats
- Linting — built-in lint rules

