pikku-addon

$npx mdskill add pikkujs/pikku/pikku-addon

Build and share reusable function packages across projects.

  • Enables bundling functions, services, and secrets into self-contained NPM packages.
  • Integrates with pikku-concepts for mental models and pikku-rpc for internal composition.
  • Decides usage based on requests for cross-project sharing or addon package structure.
  • Registers addons via wireAddon to expose functions with optional authentication and tags.

SKILL.md

.github/skills/pikku-addonView on GitHub ↗
---
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

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

```bash
pikku info functions --verbose   # See existing functions and addons
pikku info tags --verbose        # Understand project organization
```

See `pikku-concepts` for the core mental model.

## API Reference

### `wireAddon(config)`

Register an addon in the consuming project:

```typescript
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.:

```typescript
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):

```typescript
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.):

```typescript
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),
    }
  }
)
```

## Creating an Addon

### Scaffold

```bash
npx pikku new addon
```

### Package Structure

```text
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)
```

### pikku.config.json

```json
{
  "tsconfig": "./tsconfig.json",
  "srcDirectories": ["src", "types"],
  "outDir": "./.pikku",
  "addon": true,
  "node": {
    "displayName": "My Addon",
    "description": "What this addon does",
    "categories": ["General"]
  }
}
```

### package.json (key fields)

```json
{
  "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/"
  }
}
```

### Services

```typescript
// 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 {}
  }
)
```

### Functions

```typescript
// 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)
  },
})
```

### Build

```bash
npx pikku all    # Generate types
yarn tsc         # Compile TypeScript
cp -r .pikku dist/  # Include generated files in dist
```

## Consuming an Addon

### Install & Register

```bash
yarn add @my-org/addon-todos
```

```typescript
// 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.

### Call via RPC

```typescript
export const myFunc = pikkuFunc({
  func: async (_services, data, { rpc }) => {
    const todo = await rpc.invoke('todos:addTodo', { title: 'Buy milk' })
    return todo
  },
})
```

### Wire to HTTP

```typescript
import { wireHTTP, addon } from '#pikku'

wireHTTP({
  method: 'get',
  route: '/todos',
  func: addon('todos:listTodos'),
  auth: false,
})
```

### Use in AI Agents

```typescript
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,
})
```

## Complete Example

```typescript
// --- 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 },
})
```

More from pikkujs/pikku

SkillDescription
pikku-ai-agent'Use when building AI agents, chatbots, or LLM-powered assistants with Pikku. Covers pikkuAIAgent, tool registration, memory, streaming, and agent invocation.
pikku-ai-vercel'Use when setting up AI agent execution with the Vercel AI SDK in a Pikku app. Covers VercelAIAgentRunner for streaming and non-streaming AI agent steps.
pikku-ai-voice'Use when adding voice input (speech-to-text) or voice output (text-to-speech) to AI agents in a Pikku app. Covers voiceInput/voiceOutput middleware hooks and STT/TTS service interfaces.
pikku-auth-js'Use when integrating Auth.js (NextAuth) with a Pikku app. Covers createAuthHandler, createAuthRoutes, and Auth.js configuration.
pikku-aws'Use when setting up AWS services (S3, SQS, Secrets Manager) in a Pikku app. Covers S3Content for file storage, SQSQueueService for queues, and AWSSecrets for secret management.
pikku-backblaze'Use when setting up Backblaze B2 file storage in a Pikku app. Covers B2Content for file uploads, downloads, and signed URLs.
pikku-cli'Use when building CLI commands with Pikku. Covers wireCLI, pikkuCLICommand, subcommands, options, parameters, custom renderers, and nested command groups.
pikku-concepts'Foundational guide to Pikku framework concepts. Use this skill when working with any Pikku codebase, starting a new Pikku project, or migrating a backend to Pikku. Covers the core mental model, function types, project structure, code generation, testing, and how Pikku maps to traditional backend patterns.
pikku-config'Use when managing secrets, environment variables, config, or OAuth2 credentials in a Pikku app. Covers wireSecret, wireVariable, wireOAuth2Credential, and typed config access.
pikku-cron'Use when adding scheduled tasks, recurring jobs, or cron-based automation to a Pikku app. Covers wireScheduler, cron expressions, scheduled task wire object, and scheduler middleware.