packages/cli/skills/pikku-versioning/SKILL.md
Use when versioning Pikku function contracts, detecting breaking changes, or managing API backward compatibility. Covers the version property, versions.pikku.json manifest, contract hashing, and CI integration. TRIGGER when: code uses version: on a pikkuFunc, user asks about API versioning, breaking changes, contract hashes, backward compatibility, or "pikku versions" CLI commands. DO NOT TRIGGER when: user asks about secrets/variables/OAuth2 (use pikku-config) or general function definitions (use pikku-concepts).
npx skillsauth add pikkujs/pikku pikku-versioningInstall 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.Track and protect function contracts across releases. Pikku hashes each function's input/output schema into a manifest so you can detect breaking changes before they ship.
pikku info functions --verbose # See existing functions and their versions
See pikku-concepts for the core mental model.
When you need to introduce a breaking change, keep the old function as a pinned version and let the new one become the latest.
The pattern:
my-function-v1.function.ts — export a variable with the V1 suffixoverride: 'myFunction' — this is the contract key the manifest groups underversion: 1 — pins this as version 1 of the contractmy-function.function.ts (no version: field) automatically becomes the latest version// my-function-v1.function.ts — old contract, kept for running workflows/agents
export const getBookV1 = pikkuFunc({
override: 'getBook', // REQUIRED — links this to the 'getBook' contract family
version: 1,
input: z.object({ bookId: z.string() }),
output: z.object({ title: z.string() }),
func: async ({ db }, { bookId }) => {
return db.getBook(bookId)
},
})
// my-function.function.ts — latest contract, no version: field
export const getBook = pikkuFunc({
input: z.object({
bookId: z.string(),
format: z.enum(['full', 'summary']),
}),
output: z.object({
title: z.string(),
author: z.string(),
isbn: z.string(),
}),
func: async ({ db }, { bookId, format }) => {
return db.getBook(bookId, format)
},
})
Why override is required: The manifest groups functions by a shared contract key. Without override: 'getBook', getBookV1 is stored internally as getBookV1@v1 (key: getBookV1), which is a different contract family from getBook. With override: 'getBook', it becomes getBook@v1 (key: getBook), which groups with the unversioned getBook — and the unversioned one is automatically promoted to getBook@v2.
versions.pikku.json)Pikku tracks contract hashes to detect breaking changes:
{
"manifestVersion": 1,
"contracts": {
"createTodo": {
"latest": 1,
"versions": {
"1": { "inputHash": "a1b2c3d4", "outputHash": "e5f6a7b8" }
}
},
"getTodos": {
"latest": 2,
"versions": {
"1": { "inputHash": "i9j0k1l2", "outputHash": "m3n4o5p6" },
"2": { "inputHash": "q7r8s9t0", "outputHash": "u1v2w3x4" }
}
}
}
}
Each hash is derived from the function's input and output schemas plus the contract key. If a schema changes without a version bump, pikku versions check will fail.
npx pikku versions init # Initialize versioning manifest (run once)
npx pikku versions check # Detect contract changes (use in CI)
npx pikku versions update # Update contract hashes after version bump
Workflow:
pikku versions init — run once to create versions.pikku.jsonpikku versions check — CI catches unversioned breaking changesmy-function-v1.function.ts with override + version: 1, then pikku versions update# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx pikku versions check
// create-todo-v1.function.ts — v1 locked contract
export const createTodoV1 = pikkuSessionlessFunc({
override: 'createTodo', // groups under 'createTodo' contract family
version: 1,
input: z.object({ title: z.string() }),
output: z.object({ id: z.string(), title: z.string() }),
func: async ({ todoStore }, { title }) => todoStore.add(title),
})
// create-todo.function.ts — v2 (latest), called by default
export const createTodo = pikkuSessionlessFunc({
input: z.object({
title: z.string(),
priority: z.enum(['low', 'medium', 'high']),
}),
output: z.object({
id: z.string(),
title: z.string(),
priority: z.string(),
}),
func: async ({ todoStore }, { title, priority }) =>
todoStore.add(title, priority),
})
Result in manifest:
"createTodo": {
"latest": 2,
"versions": {
"1": { "inputHash": "...", "outputHash": "..." },
"2": { "inputHash": "...", "outputHash": "..." }
}
}
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.