.claude/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.
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.