.claude/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.
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.
Add version to function config to maintain backward compatibility:
// v1 — kept for running workflows and agents
const getBookV1 = pikkuFunc({
title: 'Get Book',
version: 1,
input: z.object({ bookId: z.string() }),
output: z.object({ title: z.string() }),
func: async ({ db }, { bookId }) => {
return await db.getBook(bookId)
},
})
// v2 — the latest version, called by default
const getBook = pikkuFunc({
title: 'Get Book',
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 await db.getBook(bookId, format)
},
})
When you add a breaking change (new required fields, removed fields, type changes), bump the version number on the old function and create the new version without a version field (it becomes the latest).
versions.pikku.json)Pikku tracks contract hashes to detect breaking changes:
{
"manifestVersion": 1,
"contracts": {
"createTodo": {
"latest": 1,
"versions": {
"1": "a1b2c3d4e5f6g7h8"
}
},
"getTodos": {
"latest": 2,
"versions": {
"1": "i9j0k1l2m3n4o5p6",
"2": "q7r8s9t0u1v2w3x4"
}
}
}
}
Each hash is derived from the function's input and output schemas. If a schema changes without a version bump, pikku versions check will fail.
npx pikku versions init # Initialize versioning manifest
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 changesversion on old function, 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
// v1 — original contract
export const createTodoV1 = pikkuSessionlessFunc({
title: 'Create Todo',
version: 1,
input: z.object({ title: z.string() }),
output: z.object({ id: z.string(), title: z.string() }),
func: async ({ todoStore }, { title }) => {
return todoStore.add(title)
},
})
// v2 — added priority field (breaking: new required input)
export const createTodo = pikkuSessionlessFunc({
title: 'Create Todo',
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 }) => {
return todoStore.add(title, priority)
},
})
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.
development
Make a Pikku frontend work in both English (LTR) and Arabic / right-to-left languages. Direction is derived from the active locale, applied once at the document root, and the layout mirrors itself — but only if styling is written flow-relative (margin-inline-start, text-align: start, Mantine ms/me) instead of left/right. TRIGGER when: adding Arabic (or Hebrew/Farsi/Urdu), asked to "support RTL / right-to-left / bidi / mirror the layout", or writing layout styles in an app that may run RTL. Builds on pikku-i18n (an RTL language is just another locale file). DO NOT TRIGGER for backend functions or for LTR-only copy changes.
development
Wire i18n into a Pikku frontend (Vite SPA, Vite SSR, or Next.js app-router) with react-i18next + i18next. English by default, every user-facing string goes through a `t()` token, and additional languages are served under `/de` `/es` URL prefixes. TRIGGER when: scaffolding or editing a frontend and writing user-facing text, adding a second language, or asked to "make this translatable / use tokens / add i18n". DO NOT TRIGGER for backend functions, error messages thrown from functions, or log output.
development
Use when translating an n8n Code node body into a real Pikku function body. Triggered when the user opens or points at a stub generated by @pikku/n8n-import (look for `STUB — generated from n8n Code node` in the file's JSDoc), or when the user says 'translate this n8n code', 'port this n8n code node', 'finish the codeStub__... function', etc. The stub file is a `pikkuSessionlessFunc` with a Zod input/output, a JSDoc preserving the original n8n JavaScript verbatim, and a `throw new Error('… — implement me')` body.