packages/cli/skills/pikku-addon/SKILL.md
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).
npx skillsauth add pikkujs/pikku pikku-addonInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill as an execution checklist, not reference material.
pikku-meta when available; otherwise run the relevant pikku meta ... --json command and inspect only the focused output you need..pikku, node_modules, vendored packages, or broad build artifacts.pikku-verify or pikku all when functions, wirings, schemas, or generated clients may have changed.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.
pikku info functions --verbose # See existing functions and addons
pikku info tags --verbose # Understand project organization
See pikku-concepts for the core mental model.
wireAddon(config)Register an addon in the consuming project:
import { wireAddon } from '#pikku'
wireAddon({
name: string, // Namespace for addon functions (e.g. 'todos')
package: string, // NPM package name (e.g. '@pikku/addon-todos')
rpcEndpoint?: string, // Optional remote RPC endpoint for distributed execution
auth?: boolean, // Whether addon functions require authentication
tags?: string[], // Tags applied to all addon functions
secretOverrides?: Record<string, string>, // Remap secret names
variableOverrides?: Record<string, string>, // Remap variable names
})
addon(name)Type-safe reference to an addon function — use when wiring to HTTP, agents, etc.:
import { addon } from '#pikku'
addon('todos:addTodo') // Returns a typed function config
addon('emails:sendEmail') // Namespace:functionName format
pikkuAddonServices(factory)Define singleton services for an addon package (created once at startup):
import { pikkuAddonServices } from '#pikku'
export const createSingletonServices = pikkuAddonServices(
async (config, parentServices?) => {
// parentServices: logger, variables, secrets from the consuming app
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) => {
// wire: transport context (http, channel, session, etc.)
const authHeader = wire.http?.request?.header('authorization')
return {
myService: new MyService(authHeader),
}
}
)
npx pikku new addon
my-addon/
├── package.json # Exports .pikku/* and dist/
├── pikku.config.json # addon: true + metadata
├── tsconfig.json # #pikku path mapping
├── src/
│ ├── services.ts # createSingletonServices (required)
│ └── functions/
│ └── *.function.ts # Function definitions
├── types/
│ └── application-types.d.ts # SingletonServices interface
└── .pikku/ # Generated (gitignored)
{
"tsconfig": "./tsconfig.json",
"srcDirectories": ["src", "types"],
"outDir": "./.pikku",
"addon": true,
"node": {
"displayName": "My Addon",
"description": "What this addon does",
"categories": ["General"]
}
}
{
"name": "@my-org/addon-todos",
"imports": {
"#pikku": "./.pikku/pikku-types.gen.ts",
"#pikku/*": "./.pikku/*"
},
"exports": {
".": { "types": "./dist/src/index.d.ts", "import": "./dist/src/index.js" },
"./.pikku/*": "./.pikku/*",
"./.pikku/pikku-metadata.gen.json": "./.pikku/pikku-metadata.gen.json",
"./.pikku/rpc/pikku-rpc-wirings-map.internal.gen.js": {
"types": "./.pikku/rpc/pikku-rpc-wirings-map.internal.gen.d.ts"
}
},
"files": ["dist", ".pikku"],
"peerDependencies": {
"@pikku/core": "*"
},
"scripts": {
"pikku": "pikku all",
"build": "tsc && cp -r .pikku dist/"
}
}
// src/services.ts
import { pikkuAddonServices, pikkuAddonWireServices } from '#pikku'
import { TodoStore } from './todo-store.service.js'
export const createSingletonServices = pikkuAddonServices(async () => {
const todoStore = new TodoStore()
return { todoStore }
})
// Optional — only needed if addon functions require per-request services
export const createWireServices = pikkuAddonWireServices(
async (singletonServices, wire) => {
return {}
}
)
// src/functions/addTodo.function.ts
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)
},
})
npx pikku all # Generate types
yarn tsc # Compile TypeScript
cp -r .pikku dist/ # Include generated files in dist
yarn add @my-org/addon-todos
// wirings/todos.wirings.ts
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.
export const myFunc = pikkuFunc({
func: async (_services, data, { rpc }) => {
const todo = await rpc.invoke('todos:addTodo', { title: 'Buy milk' })
return todo
},
})
import { wireHTTP, addon } from '#pikku'
wireHTTP({
method: 'get',
route: '/todos',
func: addon('todos:listTodos'),
auth: false,
})
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,
})
// --- ADDON PACKAGE: @my-org/addon-todos ---
// src/services.ts
import { pikkuAddonServices } from '#pikku'
export const createSingletonServices = pikkuAddonServices(async () => {
return { todoStore: new TodoStore() }
})
// src/functions/listTodos.function.ts
export const listTodos = pikkuSessionlessFunc({
description: 'List all todos',
func: async ({ todoStore }) => {
return { todos: todoStore.list() }
},
})
// src/functions/addTodo.function.ts
export const addTodo = pikkuSessionlessFunc({
description: 'Add a new todo',
approvalRequired: true,
approvalDescription: async (_services, { title }) => {
return `Add a todo called "${title}"`
},
input: z.object({ title: z.string() }),
output: z.object({ id: z.string(), title: z.string() }),
func: async ({ todoStore }, { title }) => {
return todoStore.add(title)
},
})
// --- CONSUMING PROJECT ---
// wirings/addons.wirings.ts
import { wireAddon } from '#pikku'
wireAddon({ name: 'todos', package: '@my-org/addon-todos' })
// wirings/api.http.ts
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 },
})
documentation
Deprecated — use pikku-middleware instead. Tag middleware (addTagMiddleware) is now documented as a section within the pikku-middleware skill, alongside global HTTP middleware, execution order, and the service-to-service bearer auth pattern.
testing
Use when adding authorization checks to Pikku functions or routes — pikkuPermission, pikkuAuth, per-function permissions, pattern-based permissions, or understanding OR/AND permission logic. TRIGGER when: user wants to restrict who can call a function, check resource ownership, add role-based access, or understand where permission checks belong. DO NOT TRIGGER when: user asks about middleware or request interception (use pikku-middleware), authentication strategies (use pikku-security), or session management.
testing
Use when adding any middleware to a Pikku app — global HTTP middleware, tag-scoped middleware (including service-to-service bearer auth), per-route middleware, session-setting middleware, or understanding middleware execution order and priority. TRIGGER when: user wants middleware on some or all routes, machine-to-machine auth, tag-scoped cross-cutting concerns, global interceptors, or middleware priority/order questions. DO NOT TRIGGER when: user asks about permissions/authorization checks (use pikku-permissions), auth strategies like authBearer/authCookie (use pikku-security), or deployment.
documentation
Standard cleanup to run right after a Pikku template is cloned or scaffolded into a new project. TRIGGER when: a Pikku template was just cloned/scaffolded (via `pikku create`, `git clone <template>`, or the user says "I cloned the kanban template / starter / template"), or the working tree still looks like an untouched template (template README, placeholder `@project/*` name in package.json). DO NOT TRIGGER when: working in an established project mid-feature, or editing the template repo itself.