| name | pikku-addon |
| description | Use when creating or consuming reusable function packages (addons) in Pikku. Covers wireAddon, addon(), pikkuAddonServices, pikkuAddonWireServices, addon package structure, and cross-project function sharing. TRIGGER when: code uses wireAddon/addon()/pikkuAddonServices, user asks about addons, reusable function packages, cross-project sharing, or addon package structure. DO NOT TRIGGER when: user asks about internal function composition (use pikku-rpc) or general function definitions (use pikku-concepts). |
Pikku Addons
Agent Operating Procedure
Use this skill as an execution checklist, not reference material.
- Discover before editing. Prefer OpenCode tools such as
pikku-meta when available; otherwise run the relevant pikku meta ... --json command and inspect only the focused output you need.
- Identify the source files that own the behavior. Do not start by reading generated output,
.pikku, node_modules, vendored packages, or broad build artifacts.
- Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
- Validate with the narrowest relevant command first, then run
pikku-verify or pikku all when functions, wirings, schemas, or generated clients may have changed.
- If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
Addons are reusable Pikku function packages that can be shared across projects. They bundle functions, services, secrets, and variables into a self-contained NPM package.
Before You Start
pikku info functions --verbose
pikku info tags --verbose
See pikku-concepts for the core mental model.
API Reference
wireAddon(config)
Register an addon in the consuming project:
import { wireAddon } from '#pikku'
wireAddon({
name: string,
package: string,
rpcEndpoint?: string,
auth?: boolean,
tags?: string[],
secretOverrides?: Record<string, string>,
variableOverrides?: Record<string, string>,
})
addon(name)
Type-safe reference to an addon function — use when wiring to HTTP, agents, etc.:
import { addon } from '#pikku'
addon('todos:addTodo')
addon('emails:sendEmail')
pikkuAddonServices(factory)
Define singleton services for an addon package (created once at startup):
import { pikkuAddonServices } from '#pikku'
export const createSingletonServices = pikkuAddonServices(
async (config, parentServices?) => {
return {
myStore: new MyStore(),
}
}
)
pikkuAddonWireServices(factory)
Define per-request services for an addon package (created fresh per HTTP request, queue job, etc.):
import { pikkuAddonWireServices } from '#pikku'
export const createWireServices = pikkuAddonWireServices(
async (singletonServices, wire) => {
const authHeader = wire.http?.request?.header('authorization')
return {
myService: new MyService(authHeader),
}
}
)
Creating an Addon
Scaffold
npx pikku new addon
This generates package.json (exports .pikku/* + dist/), pikku.config.json (addon: true), tsconfig.json (#pikku path mapping), src/services.ts, src/functions/, and types/application-types.d.ts. For the full file contents/exports you rarely hand-edit, read references/addon-package-manifest.md.
Services
import { pikkuAddonServices, pikkuAddonWireServices } from '#pikku'
import { TodoStore } from './todo-store.service.js'
export const createSingletonServices = pikkuAddonServices(async () => {
const todoStore = new TodoStore()
return { todoStore }
})
export const createWireServices = pikkuAddonWireServices(
async (singletonServices, wire) => {
return {}
}
)
Functions
import { z } from 'zod'
import { pikkuSessionlessFunc } from '#pikku'
const AddTodoInput = z.object({ title: z.string() })
const AddTodoOutput = z.object({ id: z.string(), title: z.string() })
export const addTodo = pikkuSessionlessFunc({
description: 'Adds a new todo',
input: AddTodoInput,
output: AddTodoOutput,
func: async ({ todoStore }, { title }) => {
return todoStore.add(title)
},
})
Optional approval gating (e.g. for agent tools) — add approvalRequired: true plus an approvalDescription resolver:
approvalRequired: true,
approvalDescription: async (_services, { title }) => `Add a todo called "${title}"`,
Build
npx pikku all
yarn tsc
cp -r .pikku dist/
Consuming an Addon
Install & Register
yarn add @my-org/addon-todos
import { wireAddon } from '#pikku'
wireAddon({ name: 'todos', package: '@my-org/addon-todos' })
After registration, run npx pikku all to generate types for the addon's functions.
Call via RPC
export const myFunc = pikkuFunc({
func: async (_services, data, { rpc }) => {
const todo = await rpc.invoke('todos:addTodo', { title: 'Buy milk' })
return todo
},
})
Wire to HTTP
import { wireHTTP, addon } from '#pikku'
wireHTTP({
method: 'get',
route: '/todos',
func: addon('todos:listTodos'),
auth: false,
})
Or batch multiple addon routes with defineHTTPRoutes + wireHTTPRoutes:
import { wireHTTPRoutes, defineHTTPRoutes, addon } from '#pikku'
const todoRoutes = defineHTTPRoutes({
tags: ['todos'],
auth: false,
routes: {
list: { method: 'get', route: '/todos', func: addon('todos:listTodos') },
add: { method: 'post', route: '/todos', func: addon('todos:addTodo') },
},
})
wireHTTPRoutes({ basePath: '/api', routes: { todos: todoRoutes } })
Use in AI Agents
import { pikkuAIAgent } from '#pikku'
import { addon } from '#pikku'
export const todoAgent = pikkuAIAgent({
name: 'todo-agent',
description: 'Manages a todo list',
instructions: 'You help users manage their todos.',
model: 'openai/gpt-4o',
tools: [
addon('todos:listTodos'),
addon('todos:addTodo'),
addon('todos:deleteTodo'),
],
maxSteps: 5,
})