@pikku/skills 0.12.22 → 0.12.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +134 -0
- package/dist/skills.gen.js +1 -1
- package/package.json +1 -1
- package/skills/pikku-a11y/SKILL.md +59 -0
- package/skills/pikku-addon/SKILL.md +2 -2
- package/skills/pikku-agent/SKILL.md +67 -316
- package/skills/pikku-agent/references/agents.md +299 -0
- package/skills/{pikku-ai-vercel/SKILL.md → pikku-agent/references/runner-vercel.md} +2 -24
- package/skills/{pikku-ai-voice/SKILL.md → pikku-agent/references/voice.md} +1 -22
- package/skills/pikku-architect/SKILL.md +265 -0
- package/skills/pikku-auth/SKILL.md +89 -0
- package/skills/{pikku-better-auth/SKILL.md → pikku-auth/references/better-auth.md} +126 -34
- package/skills/{pikku-jose/SKILL.md → pikku-auth/references/jose.md} +1 -23
- package/skills/{pikku-machine-auth/SKILL.md → pikku-auth/references/machine-auth.md} +0 -23
- package/skills/pikku-auth/references/permissions.md +261 -0
- package/skills/{pikku-security/SKILL.md → pikku-auth/references/sessions.md} +0 -20
- package/skills/pikku-build/SKILL.md +88 -0
- package/skills/{pikku-build-app/SKILL.md → pikku-build/references/app.md} +76 -24
- package/skills/{pikku-feature/SKILL.md → pikku-build/references/feature.md} +1 -8
- package/skills/{pikku-build-app → pikku-build}/references/multi-app.md +56 -1
- package/skills/{pikku-build-platform/SKILL.md → pikku-build/references/platform.md} +22 -37
- package/skills/{pikku-template-clone/SKILL.md → pikku-build/references/post-clone.md} +0 -6
- package/skills/{pikku-build-quick/SKILL.md → pikku-build/references/quick.md} +4 -20
- package/skills/{pikku-build-app → pikku-build}/references/ship.md +9 -3
- package/skills/pikku-concepts/SKILL.md +72 -7
- package/skills/pikku-concepts/references/concept-mapping.md +8 -8
- package/skills/pikku-deploy/SKILL.md +158 -0
- package/skills/{pikku-deploy-azure/SKILL.md → pikku-deploy/references/azure.md} +18 -50
- package/skills/pikku-deploy/references/cloudflare.md +104 -0
- package/skills/pikku-deploy/references/express.md +92 -0
- package/skills/{pikku-deploy-fastify/SKILL.md → pikku-deploy/references/fastify.md} +8 -32
- package/skills/{pikku-deploy-lambda/SKILL.md → pikku-deploy/references/lambda.md} +6 -27
- package/skills/{pikku-deploy-nextjs/SKILL.md → pikku-deploy/references/nextjs.md} +9 -33
- package/skills/pikku-deploy/references/uws.md +72 -0
- package/skills/pikku-deploy/references/ws.md +75 -0
- package/skills/pikku-emails/SKILL.md +3 -2
- package/skills/pikku-fabric/SKILL.md +47 -20
- package/skills/{pikku-fabric-debug/SKILL.md → pikku-fabric/references/debugging.md} +0 -6
- package/skills/pikku-i18n/SKILL.md +62 -207
- package/skills/{pikku-paraglide/SKILL.md → pikku-i18n/references/enum-labels.md} +0 -6
- package/skills/pikku-i18n/references/messages.md +218 -0
- package/skills/{pikku-rtl/SKILL.md → pikku-i18n/references/rtl.md} +3 -9
- package/skills/pikku-knowledge/SKILL.md +15 -0
- package/skills/pikku-kysely/SKILL.md +13 -13
- package/skills/pikku-list-query/SKILL.md +163 -0
- package/skills/pikku-meta/SKILL.md +58 -130
- package/skills/{pikku-deps/SKILL.md → pikku-meta/references/audit.md} +1 -17
- package/skills/pikku-meta/references/meta.md +114 -0
- package/skills/{pikku-versioning/SKILL.md → pikku-meta/references/versioning.md} +0 -26
- package/skills/pikku-middleware/SKILL.md +5 -5
- package/skills/pikku-n8n-import/SKILL.md +0 -1
- package/skills/pikku-permissions/SKILL.md +75 -229
- package/skills/pikku-react/SKILL.md +50 -298
- package/skills/pikku-react/references/client.md +313 -0
- package/skills/{pikku-react-query/SKILL.md → pikku-react/references/react-query.md} +2 -22
- package/skills/{pikku-workflows-client/SKILL.md → pikku-react/references/workflows.md} +1 -22
- package/skills/pikku-realtime/SKILL.md +110 -251
- package/skills/pikku-scenario/SKILL.md +60 -45
- package/skills/pikku-scenario/references/persona-run.md +148 -0
- package/skills/pikku-seo/SKILL.md +133 -0
- package/skills/pikku-service-backends/SKILL.md +154 -0
- package/skills/pikku-service-backends/references/aws.md +106 -0
- package/skills/pikku-service-backends/references/backblaze.md +57 -0
- package/skills/pikku-service-backends/references/mongodb.md +90 -0
- package/skills/pikku-service-backends/references/redis.md +75 -0
- package/skills/pikku-service-backends/references/schema.md +63 -0
- package/skills/pikku-services/SKILL.md +68 -291
- package/skills/{pikku-audit/SKILL.md → pikku-services/references/audit.md} +0 -22
- package/skills/{pikku-config/SKILL.md → pikku-services/references/config.md} +1 -25
- package/skills/{pikku-pino/SKILL.md → pikku-services/references/pino.md} +0 -20
- package/skills/pikku-services/references/services.md +272 -0
- package/skills/pikku-software-archaeology/README.md +5 -1
- package/skills/pikku-software-archaeology/SKILL.md +16 -2
- package/skills/{pikku-product-second-opinion/example/sample-report.md → pikku-software-archaeology/example/second-opinion-sample-report.md} +1 -1
- package/skills/pikku-software-archaeology/references/blueprint.schema.json +1 -1
- package/skills/pikku-software-archaeology/references/pikku-mapping.md +3 -3
- package/skills/{pikku-product-second-opinion/SKILL.md → pikku-software-archaeology/references/second-opinion.md} +4 -9
- package/skills/pikku-webhook/SKILL.md +224 -0
- package/skills/pikku-wiring/SKILL.md +180 -0
- package/skills/{pikku-websocket/SKILL.md → pikku-wiring/references/channel.md} +2 -35
- package/skills/{pikku-cli/SKILL.md → pikku-wiring/references/cli.md} +1 -33
- package/skills/{pikku-gateway-slack/SKILL.md → pikku-wiring/references/gateway-slack.md} +0 -23
- package/skills/{pikku-http/SKILL.md → pikku-wiring/references/http.md} +3 -39
- package/skills/{pikku-mcp/SKILL.md → pikku-wiring/references/mcp.md} +0 -33
- package/skills/{pikku-queue/SKILL.md → pikku-wiring/references/queue.md} +1 -33
- package/skills/pikku-wiring/references/realtime.md +265 -0
- package/skills/{pikku-rpc/SKILL.md → pikku-wiring/references/rpc.md} +0 -32
- package/skills/{pikku-schedule/SKILL.md → pikku-wiring/references/scheduler.md} +1 -35
- package/skills/{pikku-trigger/SKILL.md → pikku-wiring/references/trigger.md} +0 -43
- package/skills/pikku-workflow/SKILL.md +39 -2
- package/skills/pikku-aws/SKILL.md +0 -161
- package/skills/pikku-backblaze/SKILL.md +0 -104
- package/skills/pikku-deploy-cloudflare/SKILL.md +0 -123
- package/skills/pikku-deploy-express/SKILL.md +0 -122
- package/skills/pikku-deploy-uws/SKILL.md +0 -144
- package/skills/pikku-mongodb/SKILL.md +0 -113
- package/skills/pikku-product-second-opinion/README.md +0 -43
- package/skills/pikku-redis/SKILL.md +0 -99
- package/skills/pikku-schema-ajv/SKILL.md +0 -83
- package/skills/pikku-schema-cfworker/SKILL.md +0 -82
- package/skills/pikku-ws/SKILL.md +0 -87
- /package/skills/{pikku-build-app → pikku-build}/references/theming.md +0 -0
- /package/skills/{pikku-product-second-opinion/references/report-template.md → pikku-software-archaeology/references/second-opinion-report-template.md} +0 -0
- /package/skills/{pikku-cli/references/complete-example.md → pikku-wiring/references/cli-complete-example.md} +0 -0
- /package/skills/{pikku-http → pikku-wiring}/references/http-options.md +0 -0
- /package/skills/{pikku-realtime/references/other-routes.md → pikku-wiring/references/realtime-other-routes.md} +0 -0
|
@@ -9,9 +9,9 @@ description: >-
|
|
|
9
9
|
non-trivial kysely query (a join, an aggregate/count/sum, groupBy, subquery, transaction, or
|
|
10
10
|
conditional query), the injected `kysely` service is used in a function body, or code uses
|
|
11
11
|
PikkuKysely, KyselyChannelStore, KyselyWorkflowService, KyselySecretService, or the user asks
|
|
12
|
-
about SQL setup with Pikku. DO NOT TRIGGER when: user asks about MongoDB
|
|
13
|
-
|
|
14
|
-
installGroups: [
|
|
12
|
+
about SQL setup with Pikku. DO NOT TRIGGER when: user asks about MongoDB or Redis-backed
|
|
13
|
+
services (use pikku-service-backends).
|
|
14
|
+
installGroups: [core]
|
|
15
15
|
---
|
|
16
16
|
|
|
17
17
|
# Pikku Kysely (SQL Database Services)
|
|
@@ -216,15 +216,15 @@ Each database variant exports these services with a prefix (`Pg`, `MySQL`, `SQLi
|
|
|
216
216
|
A handful more live only on the base package — there is no `Pg`/`MySQL`/`SQLite`
|
|
217
217
|
variant to reach for, you import them from `@pikku/kysely` whatever the engine:
|
|
218
218
|
|
|
219
|
-
| Service | Purpose
|
|
220
|
-
| ---------------------------- |
|
|
221
|
-
| `KyselySessionStore` | Persisted user sessions
|
|
222
|
-
| `KyselyScopeService` | Scope and role storage
|
|
223
|
-
| `KyselyWebhookService` |
|
|
224
|
-
| `KyselyCredentialService` | Encrypted third-party credentials
|
|
225
|
-
| `KyselyAgentRunStateService` | AI run state (also implemented by AIStorage)
|
|
226
|
-
| `KyselyWorkflowMirror` | Mirrors workflow runs into queryable tables
|
|
227
|
-
| `KyselyAuditService` | Durable audit sink (see `pikku-
|
|
219
|
+
| Service | Purpose |
|
|
220
|
+
| ---------------------------- | ------------------------------------------------------------------- |
|
|
221
|
+
| `KyselySessionStore` | Persisted user sessions |
|
|
222
|
+
| `KyselyScopeService` | Scope and role storage |
|
|
223
|
+
| `KyselyWebhookService` | Outgoing webhook deliveries + attempt history (see `pikku-webhook`) |
|
|
224
|
+
| `KyselyCredentialService` | Encrypted third-party credentials |
|
|
225
|
+
| `KyselyAgentRunStateService` | AI run state (also implemented by AIStorage) |
|
|
226
|
+
| `KyselyWorkflowMirror` | Mirrors workflow runs into queryable tables |
|
|
227
|
+
| `KyselyAuditService` | Durable audit sink (see `pikku-services`) |
|
|
228
228
|
|
|
229
229
|
All services take a `Kysely<KyselyPikkuDB>` instance in their constructor and have an `init()` method that creates tables if needed.
|
|
230
230
|
|
|
@@ -257,7 +257,7 @@ const newVersion = await secrets.rotateKEK()
|
|
|
257
257
|
|
|
258
258
|
`getSecret` hands back a `SecretValue<T>`, not the bare value — it serializes as
|
|
259
259
|
`[secret]` until something reveals it, which is what stops a secret drifting into
|
|
260
|
-
a log line or an audit row. See `pikku-
|
|
260
|
+
a log line or an audit row. See `pikku-services` for the reveal rules.
|
|
261
261
|
|
|
262
262
|
## Usage Patterns
|
|
263
263
|
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pikku-list-query
|
|
3
|
+
description: >-
|
|
4
|
+
Use when building a paginated/infinite-scroll list — any RPC that returns rows a user scrolls through (tables, card grids, search results). Covers pikkuListFunc, the ListInput/ListOutput cursor contract, and the generated usePikkuInfiniteQuery hook.
|
|
5
|
+
TRIGGER when: user asks for infinite scroll, "load more", a paginated table/list/grid, or a list that could grow beyond a single page.
|
|
6
|
+
DO NOT TRIGGER when: the list is small and fixed (e.g. a settings page with 5 items) — a plain pikkuFunc + usePikkuQuery returning a full array is simpler and correct there.
|
|
7
|
+
installGroups: [core, client]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Pikku List Queries
|
|
11
|
+
|
|
12
|
+
## Agent Operating Procedure
|
|
13
|
+
|
|
14
|
+
1. Capture baseline. Run `pikku all` BEFORE writing code; only NEW errors are yours to fix.
|
|
15
|
+
2. Write the backend function with `pikkuListFunc` (below) — never a bespoke `{items: [...]}` shape once the list can page.
|
|
16
|
+
3. Run `pikku all` to regenerate `usePikkuInfiniteQuery` for the new function.
|
|
17
|
+
4. Wire the frontend with `usePikkuInfiniteQuery`, not a hand-rolled `useState` page counter and not a raw `useInfiniteQuery` — the generated hook already resolves cursor plumbing from your function's types.
|
|
18
|
+
5. Validate with `pikku all`.
|
|
19
|
+
|
|
20
|
+
## The `pikkuListFunc` contract
|
|
21
|
+
|
|
22
|
+
A list function is a normal `pikkuFunc`/`pikkuSessionlessFunc` whose input/output conform to two shared shapes from `@pikku/core`:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
interface ListInput<F extends Record<string, unknown> = {}, S extends string = never> {
|
|
26
|
+
cursor?: string // opaque — echo back whatever you returned as nextCursor
|
|
27
|
+
limit?: number // page size; server may cap it
|
|
28
|
+
sort?: Array<{ column: S; direction: 'asc' | 'desc' }>
|
|
29
|
+
filter?: Filter<F> // structured AND/OR tree, Prisma-style leaf operators
|
|
30
|
+
search?: string // free-text search across server-chosen fields
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface ListOutput<Row> {
|
|
34
|
+
rows: Row[]
|
|
35
|
+
nextCursor: string | null // null = no more pages
|
|
36
|
+
totalCount?: number // optional — skip when expensive to compute
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Adopting this shape is what makes the function eligible for the generated `usePikkuInfiniteQuery` hook — the react-query codegen structurally detects any RPC whose output includes `nextCursor` and generates an infinite-query hook for it automatically. No manual wiring, no opt-in flag.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { pikkuListFunc } from '#pikku/function'
|
|
44
|
+
|
|
45
|
+
interface Item {
|
|
46
|
+
id: string
|
|
47
|
+
label: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const listItems = pikkuListFunc<{ status?: string }, Item>({
|
|
51
|
+
expose: true,
|
|
52
|
+
auth: true,
|
|
53
|
+
readonly: true,
|
|
54
|
+
description: 'List items for the signed-in user, paginated.',
|
|
55
|
+
// `input` is inferred as ListInput<{ status?: string }> from the generics above —
|
|
56
|
+
// never re-annotate it inline.
|
|
57
|
+
func: async ({ kysely }, input, { session }) => {
|
|
58
|
+
// `limit` is caller-supplied on an exposed RPC, so it is CAPPED, not trusted —
|
|
59
|
+
// `ListInput` says "server may cap" and this is where that happens.
|
|
60
|
+
const limit = Math.min(Math.max(Math.trunc(input.limit ?? 20) || 20, 1), 100)
|
|
61
|
+
const parsed = input.cursor ? Number(input.cursor) : 0
|
|
62
|
+
const offset = Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : 0
|
|
63
|
+
|
|
64
|
+
let query = kysely.selectFrom('item').where('userId', '=', session!.userId)
|
|
65
|
+
const status = leafEquals(input.filter, 'status')
|
|
66
|
+
if (status !== undefined) {
|
|
67
|
+
query = query.where('status', '=', status)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const rows = await query.orderBy('createdAt', 'desc').offset(offset).limit(limit).execute()
|
|
71
|
+
const nextOffset = offset + rows.length
|
|
72
|
+
const totalCount = await query
|
|
73
|
+
.select((eb) => eb.fn.countAll<number>().as('count'))
|
|
74
|
+
.executeTakeFirstOrThrow()
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
rows: rows.map((r) => ({ id: r.id, label: r.label })),
|
|
78
|
+
nextCursor: nextOffset < totalCount.count ? String(nextOffset) : null,
|
|
79
|
+
totalCount: totalCount.count,
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Cursor doesn't have to be a numeric offset — any opaque string works (a keyset value, an encoded timestamp, etc.), as long as you can turn it back into a query position on the next call.
|
|
86
|
+
|
|
87
|
+
## Frontend: `usePikkuInfiniteQuery`
|
|
88
|
+
|
|
89
|
+
Generated automatically alongside `usePikkuQuery`/`usePikkuMutation` once `reactQueryFile` is configured (see the react-query wiring docs) — no separate setup for list functions specifically.
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { usePikkuInfiniteQuery } from '.pikku/pikku-react-query.gen'
|
|
93
|
+
|
|
94
|
+
function ItemList() {
|
|
95
|
+
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = usePikkuInfiniteQuery(
|
|
96
|
+
'listItems',
|
|
97
|
+
{ limit: 20 }, // never pass cursor here — the hook manages it
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
const rows = data?.pages.flatMap((page) => page.rows) ?? []
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<>
|
|
104
|
+
{rows.map((row) => (
|
|
105
|
+
<div key={row.id}>{row.label}</div>
|
|
106
|
+
))}
|
|
107
|
+
{hasNextPage && (
|
|
108
|
+
<button disabled={isFetchingNextPage} onClick={() => fetchNextPage()}>
|
|
109
|
+
Load more
|
|
110
|
+
</button>
|
|
111
|
+
)}
|
|
112
|
+
</>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
For scroll-triggered loading (rather than a button), pair it with an `IntersectionObserver` sentinel at the end of the list that calls `fetchNextPage()` when it enters the viewport and `hasNextPage` is true — don't poll on a scroll event handler.
|
|
118
|
+
|
|
119
|
+
## Common mistakes
|
|
120
|
+
|
|
121
|
+
- **Bespoke output shape** (`{items, total}` with no `nextCursor`) — compiles, but disqualifies the function from `usePikkuInfiniteQuery`; you're left hand-rolling pagination state. Use `ListOutput<Row>`'s field names (`rows`, `nextCursor`) even if you don't need `filter`/`sort`/`search` yet — they're optional.
|
|
122
|
+
- **Fixed large `limit` instead of real pagination** (e.g. `{ limit: 500 }` fetched once) — works until the collection outgrows the cap, then silently truncates. If a list can grow unbounded, page it from the start.
|
|
123
|
+
- **Passing `cursor` manually into `usePikkuInfiniteQuery`'s input argument** — the hook injects it into each page request itself; the input you pass is the _base_ filter/limit shared by every page.
|
|
124
|
+
|
|
125
|
+
## `filter` is a TREE, not a bag of fields
|
|
126
|
+
|
|
127
|
+
`Filter<F>` is recursive: an **array** is an AND of its children, a **multi-key object** is
|
|
128
|
+
an OR keyed by labels that mean nothing at evaluation time, and only a **single-key object**
|
|
129
|
+
is a leaf. A leaf's value is either the value itself or an operator object
|
|
130
|
+
(`{ contains, in, gt, gte, lt, lte, not, startsWith, … }`).
|
|
131
|
+
|
|
132
|
+
So `'status' in input.filter` answers `false` for `[{ status: 'open' }, { userId: 'u1' }]`
|
|
133
|
+
and for `{ status: { in: ['open', 'held'] } }` — the first because the filter is an array,
|
|
134
|
+
the second because the value is an operator object rather than the string the code then
|
|
135
|
+
compares. Both cases **silently return unfiltered rows**, which on a list endpoint means
|
|
136
|
+
handing back records the caller asked to exclude. Pikku ships no filter-to-SQL helper: the
|
|
137
|
+
backend decides what it accepts, and it has to say so.
|
|
138
|
+
|
|
139
|
+
Read exactly the shape you support, and refuse the rest rather than ignoring it:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import type { Filter } from '@pikku/core/function'
|
|
143
|
+
|
|
144
|
+
/** The one shape this endpoint accepts: a single-key leaf with a plain value. */
|
|
145
|
+
function leafEquals<F extends Record<string, unknown>, K extends keyof F & string>(
|
|
146
|
+
filter: Filter<F> | undefined,
|
|
147
|
+
field: K,
|
|
148
|
+
): F[K] | undefined {
|
|
149
|
+
if (!filter || Array.isArray(filter)) return undefined
|
|
150
|
+
const keys = Object.keys(filter)
|
|
151
|
+
if (keys.length !== 1 || keys[0] !== field) return undefined
|
|
152
|
+
const value = (filter as Record<string, unknown>)[field]
|
|
153
|
+
if (value !== null && typeof value === 'object') {
|
|
154
|
+
throw new Error(`filter.${field} takes a value, not an operator object`)
|
|
155
|
+
}
|
|
156
|
+
return value as F[K]
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Supporting AND/OR or operators means walking the tree properly — recurse into the array and
|
|
161
|
+
the multi-key object, and map each leaf operator to its Kysely equivalent. Do that when the
|
|
162
|
+
UI needs it; until then, throwing on the shapes you do not handle is what stops a filter
|
|
163
|
+
from being quietly dropped.
|
|
@@ -1,139 +1,67 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pikku-meta
|
|
3
3
|
description: >-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
Use to inspect or evolve a project you did not just write — `pikku meta` and `pikku info` for
|
|
5
|
+
what the project declares (functions, schemas, wires, workflows, middleware, permissions) and
|
|
6
|
+
`pikku meta apply` to change it, `pikku versions` / `pikku semver` for contract hashes,
|
|
7
|
+
breaking-change detection and the semver a release should get, and `pikku audit` / `pikku
|
|
8
|
+
update` for dependency advisories and moving Pikku forward. TRIGGER when: user asks what
|
|
9
|
+
functions or routes exist, wants a function's input/output shape, wants to retag a function or
|
|
10
|
+
set config on a declaration, asks about API versioning, breaking changes, what semver a release
|
|
11
|
+
deserves, dependency vulnerabilities, the console Security screen, or upgrading Pikku. DO NOT
|
|
12
|
+
TRIGGER when: user is writing a new function or wiring (use the wiring skill) or asking about
|
|
13
|
+
Pikku concepts (use pikku-concepts).
|
|
11
14
|
installGroups: [core]
|
|
12
15
|
allowed-tools: Bash(yarn pikku meta *), Bash(yarn pikku info *)
|
|
13
|
-
argument-hint: '[context|functions|schemas|workflows|middleware|permissions|wires|apply]'
|
|
16
|
+
argument-hint: '[context|functions|schemas|workflows|middleware|permissions|wires|apply|versions|semver|audit|update]'
|
|
14
17
|
---
|
|
15
18
|
|
|
16
19
|
# Pikku Project Metadata
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"kind": "functionConfig",
|
|
66
|
-
"sourceFile": "src/functions/todos.functions.ts",
|
|
67
|
-
"exportedName": "listTodos",
|
|
68
|
-
"changes": { "title": "List Todos", "tags": ["todos", "read"] }
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
{
|
|
72
|
-
"kind": "functionConfig",
|
|
73
|
-
"sourceFile": "src/functions/todos.functions.ts",
|
|
74
|
-
"exportedName": "listTodos",
|
|
75
|
-
"changes": {
|
|
76
|
-
"permissions": {
|
|
77
|
-
"functionLevel": {
|
|
78
|
-
"name": "isTodoOwner",
|
|
79
|
-
"from": "../permissions.js"
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Three kinds: `functionConfig`, `agentConfig`, `functionBody`. Every operation names
|
|
89
|
-
a `sourceFile` and the `exportedName` declared in it.
|
|
90
|
-
|
|
91
|
-
`functionConfig` changes: `title`, `description`, `summary`, `tags`, `errors`,
|
|
92
|
-
`expose`, `remote`, `mcp`, `readonly`, `approvalRequired`, `permissions`.
|
|
93
|
-
`agentConfig` changes: `name`, `description`, `instructions`, `role`, `personality`,
|
|
94
|
-
`goal`, `model`, `maxSteps`, `temperature`, `toolChoice`, `tools`, `tags`.
|
|
95
|
-
|
|
96
|
-
`null` removes a property. Edits are spliced into the original text, so formatting,
|
|
97
|
-
comments and JSDoc survive.
|
|
98
|
-
|
|
99
|
-
`permissions` and `tools` are written as identifiers rather than literals, so each
|
|
100
|
-
one carries the module it comes from (`{"name": "isTodoOwner", "from": "../permissions.js"}`)
|
|
101
|
-
and the missing import is added for you — widening an existing import from that
|
|
102
|
-
module rather than adding a second one.
|
|
103
|
-
|
|
104
|
-
### Why batch
|
|
105
|
-
|
|
106
|
-
The whole batch either lands or it does not: every operation is resolved before
|
|
107
|
-
anything is written, so a failure leaves every file untouched and names the
|
|
108
|
-
operation that caused it. Batching is also what makes one codegen pass correct —
|
|
109
|
-
**run `pikku all` once after the batch**, not once per property. The response tells
|
|
110
|
-
you whether it is needed:
|
|
111
|
-
|
|
112
|
-
```json
|
|
113
|
-
{
|
|
114
|
-
"schemaVersion": "meta-apply.v1",
|
|
115
|
-
"applied": 2,
|
|
116
|
-
"files": ["src/functions/todos.functions.ts"],
|
|
117
|
-
"generatedMetaIsStale": true
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
## Human-readable tables (`pikku info`)
|
|
122
|
-
|
|
123
|
-
Four subcommands only — `functions`, `tags`, `middleware`, `permissions`. Routes,
|
|
124
|
-
channels, schedulers and queues are not subcommands; they are the _transport_ column
|
|
125
|
-
of `info functions --verbose`.
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
yarn pikku info functions --verbose --silent
|
|
129
|
-
yarn pikku info tags --silent
|
|
130
|
-
yarn pikku info middleware --verbose --silent
|
|
131
|
-
yarn pikku info permissions --verbose --silent
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
`--silent` suppresses the banner and inspector diagnostics. It works, but it is not
|
|
135
|
-
declared as an option, so every run also prints `Warning: Unknown option: --silent
|
|
136
|
-
(ignored)` — the warning is wrong. Ignore that one line.
|
|
137
|
-
|
|
138
|
-
`--limit N` caps rows (default 50); the footer says how many were withheld.
|
|
139
|
-
On `tags`, `--verbose` swaps counts for names; elsewhere it adds columns.
|
|
21
|
+
The project already knows what it declares. Ask it rather than grepping for it,
|
|
22
|
+
and change it through the write path rather than by hand.
|
|
23
|
+
|
|
24
|
+
## Pick the reference
|
|
25
|
+
|
|
26
|
+
| You are… | Read |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| Asking what exists, or setting config on a declaration | `references/meta.md` |
|
|
29
|
+
| Versioning a contract, or deciding a release's semver | `references/versioning.md` |
|
|
30
|
+
| Chasing a dependency advisory, or upgrading Pikku | `references/audit.md` |
|
|
31
|
+
|
|
32
|
+
## Start with `pikku meta context`
|
|
33
|
+
|
|
34
|
+
It answers in one call what a planner needs — functions, wires, middleware,
|
|
35
|
+
permissions, workflows, capabilities, layout. Reach for `pikku meta` when you
|
|
36
|
+
are going to act on the output and `pikku info` when a person will read it;
|
|
37
|
+
they are the same ground in two shapes.
|
|
38
|
+
|
|
39
|
+
## Direction decides whether a change is breaking
|
|
40
|
+
|
|
41
|
+
An input is contravariant (the caller writes it) and an output is covariant (the
|
|
42
|
+
caller reads it), so the same edit is not the same event on both. Adding a
|
|
43
|
+
required field breaks an input and is compatible on an output; making a field
|
|
44
|
+
optional is the reverse. `pikku semver` reads the generated JSON Schemas with
|
|
45
|
+
that asymmetry built in, so let it decide rather than eyeballing a diff.
|
|
46
|
+
|
|
47
|
+
## What NOT to do
|
|
48
|
+
|
|
49
|
+
- **Do not infer a function's input or output by reading its body**, and do not
|
|
50
|
+
cast a call site to make it compile. The schema is the type; `pikku meta
|
|
51
|
+
functions get <id>` has it.
|
|
52
|
+
- **Do not expect an unversioned function to be promoted for you.** Without an
|
|
53
|
+
explicit `version: 2` it is version 1 of its contract, collides with the
|
|
54
|
+
pinned `@v1`, and `pikku versions check` reports the published contract as
|
|
55
|
+
modified.
|
|
56
|
+
- **Do not reach for `override` by default.** The contract key already drops a
|
|
57
|
+
matching `V<n>` suffix from the export name, so `getBookV1` keys under
|
|
58
|
+
`getBook`. `override` is for an export that cannot follow that convention.
|
|
59
|
+
- **Do not shell out to the package manager from a function.** The audit is a
|
|
60
|
+
generated artifact — read `.pikku/audit.json` through
|
|
61
|
+
`metaService.readFile('audit.json')`.
|
|
62
|
+
- **Do not redeclare the audit report's shape.** `SecurityAuditReport` and its
|
|
63
|
+
companions come from `@pikku/core`; the CLI writes it, the addon reads it, the
|
|
64
|
+
UI renders it.
|
|
65
|
+
- **Do not treat a failed audit run as a clean one.** `bun audit` exits non-zero
|
|
66
|
+
when it *finds* advisories and still writes its payload, so non-zero with
|
|
67
|
+
output is data; non-zero with no output throws on purpose.
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pikku-deps
|
|
3
|
-
description: >-
|
|
4
|
-
Use for the Pikku dependency security audit: the `pikku audit` CLI command, the
|
|
5
|
-
`.pikku/audit.json` artifact, the `SecurityAuditReport` type in @pikku/core, and the console
|
|
6
|
-
Security screen (getSecurityAudit / runSecurityAudit / updateDependency + SecurityAuditView).
|
|
7
|
-
Also covers `pikku update`, which moves the @pikku/* dependency set forward and reports the
|
|
8
|
-
peers those versions need.
|
|
9
|
-
TRIGGER when: user asks about `pikku audit` or `pikku update`, dependency
|
|
10
|
-
vulnerabilities/advisories, outdated dependencies, upgrading Pikku itself, peer dependency
|
|
11
|
-
conflicts, the Security screen/page in the console, updating a vulnerable dependency, or
|
|
12
|
-
reading/rendering audit.json. DO NOT TRIGGER when: user asks about authentication/sessions/JWT
|
|
13
|
-
(use pikku-security), permissions (use pikku-permissions), or secrets/env vars (use
|
|
14
|
-
pikku-config).
|
|
15
|
-
---
|
|
16
|
-
|
|
17
1
|
# Pikku Dependency Audit
|
|
18
2
|
|
|
19
3
|
## Agent Operating Procedure
|
|
@@ -98,7 +82,7 @@ lockfiles, because that field states intent before a lockfile exists and a
|
|
|
98
82
|
project can carry a stale one from another tool. Guessing wrong is not a soft
|
|
99
83
|
failure: the spawn dies with `Executable not found in $PATH`.
|
|
100
84
|
Like every console RPC these require an **authenticated session** (the console
|
|
101
|
-
is admin-only), so the host must have Better Auth wired — see `pikku-
|
|
85
|
+
is admin-only), so the host must have Better Auth wired — see `pikku-auth`.
|
|
102
86
|
|
|
103
87
|
- `getSecurityAudit` — reads `.pikku/audit.json`, returns the report (or `null`).
|
|
104
88
|
- `runSecurityAudit` — runs `pikku audit --outdated` server-side (regenerates the
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Pikku Project Metadata
|
|
2
|
+
|
|
3
|
+
`pikku meta` is the machine-readable view of the project and the write path to it.
|
|
4
|
+
`pikku info` is the same ground as human-readable tables. Prefer `meta` when you are
|
|
5
|
+
going to act on the output; prefer `info` when a person is going to read it.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Reading
|
|
9
|
+
|
|
10
|
+
| Command | What it answers |
|
|
11
|
+
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
12
|
+
| `pikku meta context` | Everything a planner needs in one call — functions, wires, middleware, permissions, workflows, capabilities, layout. Start here. |
|
|
13
|
+
| `pikku meta functions get <id>` | One function's input/output schema names, source file, tags, expose/readonly |
|
|
14
|
+
| `pikku meta schemas get <name>` | One generated JSON schema |
|
|
15
|
+
| `pikku meta workflows get <id>` | One workflow's steps |
|
|
16
|
+
| `pikku meta permissions list` | What permissions exist and where they are defined |
|
|
17
|
+
| `pikku meta middleware list` | What middleware exists |
|
|
18
|
+
| `pikku meta wires list` | Wires by transport (http, channel, scheduler, queue, trigger) |
|
|
19
|
+
| `pikku meta clients` | Exposed RPCs/workflows/channels with their type names — what a frontend can call |
|
|
20
|
+
|
|
21
|
+
`list` is the default for each group, so `pikku meta functions` and `pikku meta functions list`
|
|
22
|
+
are the same call.
|
|
23
|
+
|
|
24
|
+
A function's input/output shape comes from here. Do not infer it by reading the
|
|
25
|
+
function body, and do not cast a call site to make it compile — the schema is the type.
|
|
26
|
+
|
|
27
|
+
## Changing
|
|
28
|
+
|
|
29
|
+
`pikku meta apply` applies a batch of edits to your own source. Pass JSON as a file
|
|
30
|
+
or on stdin:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pikku meta apply ops.json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"operations": [
|
|
39
|
+
{
|
|
40
|
+
"kind": "functionConfig",
|
|
41
|
+
"sourceFile": "src/functions/todos.functions.ts",
|
|
42
|
+
"exportedName": "listTodos",
|
|
43
|
+
"changes": { "title": "List Todos", "tags": ["todos", "read"] }
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
{
|
|
47
|
+
"kind": "functionConfig",
|
|
48
|
+
"sourceFile": "src/functions/todos.functions.ts",
|
|
49
|
+
"exportedName": "listTodos",
|
|
50
|
+
"changes": {
|
|
51
|
+
"permissions": {
|
|
52
|
+
"functionLevel": {
|
|
53
|
+
"name": "isTodoOwner",
|
|
54
|
+
"from": "../permissions.js"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Three kinds: `functionConfig`, `agentConfig`, `functionBody`. Every operation names
|
|
64
|
+
a `sourceFile` and the `exportedName` declared in it.
|
|
65
|
+
|
|
66
|
+
`functionConfig` changes: `title`, `description`, `summary`, `tags`, `errors`,
|
|
67
|
+
`expose`, `remote`, `mcp`, `readonly`, `approvalRequired`, `permissions`.
|
|
68
|
+
`agentConfig` changes: `name`, `description`, `instructions`, `role`, `personality`,
|
|
69
|
+
`goal`, `model`, `maxSteps`, `temperature`, `toolChoice`, `tools`, `tags`.
|
|
70
|
+
|
|
71
|
+
`null` removes a property. Edits are spliced into the original text, so formatting,
|
|
72
|
+
comments and JSDoc survive.
|
|
73
|
+
|
|
74
|
+
`permissions` and `tools` are written as identifiers rather than literals, so each
|
|
75
|
+
one carries the module it comes from (`{"name": "isTodoOwner", "from": "../permissions.js"}`)
|
|
76
|
+
and the missing import is added for you — widening an existing import from that
|
|
77
|
+
module rather than adding a second one.
|
|
78
|
+
|
|
79
|
+
### Why batch
|
|
80
|
+
|
|
81
|
+
The whole batch either lands or it does not: every operation is resolved before
|
|
82
|
+
anything is written, so a failure leaves every file untouched and names the
|
|
83
|
+
operation that caused it. Batching is also what makes one codegen pass correct —
|
|
84
|
+
**run `pikku all` once after the batch**, not once per property. The response tells
|
|
85
|
+
you whether it is needed:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"schemaVersion": "meta-apply.v1",
|
|
90
|
+
"applied": 2,
|
|
91
|
+
"files": ["src/functions/todos.functions.ts"],
|
|
92
|
+
"generatedMetaIsStale": true
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Human-readable tables (`pikku info`)
|
|
97
|
+
|
|
98
|
+
Four subcommands only — `functions`, `tags`, `middleware`, `permissions`. Routes,
|
|
99
|
+
channels, schedulers and queues are not subcommands; they are the _transport_ column
|
|
100
|
+
of `info functions --verbose`.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
yarn pikku info functions --verbose --silent
|
|
104
|
+
yarn pikku info tags --silent
|
|
105
|
+
yarn pikku info middleware --verbose --silent
|
|
106
|
+
yarn pikku info permissions --verbose --silent
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`--silent` suppresses the banner and inspector diagnostics. It works, but it is not
|
|
110
|
+
declared as an option, so every run also prints `Warning: Unknown option: --silent
|
|
111
|
+
(ignored)` — the warning is wrong. Ignore that one line.
|
|
112
|
+
|
|
113
|
+
`--limit N` caps rows (default 50); the footer says how many were withheld.
|
|
114
|
+
On `tags`, `--verbose` swaps counts for names; elsewhere it adds columns.
|
|
@@ -1,31 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pikku-versioning
|
|
3
|
-
description: >-
|
|
4
|
-
Use when versioning Pikku function contracts, detecting breaking changes, or managing API
|
|
5
|
-
backward compatibility. Covers the version property, versions.pikku.json manifest, contract
|
|
6
|
-
hashing, and CI integration. Also covers `pikku semver`, which derives a release's semver by
|
|
7
|
-
diffing this build's surface against a deployed one and writes .pikku/changes.gen.json.
|
|
8
|
-
TRIGGER when: code uses version: on a pikkuFunc, user asks about
|
|
9
|
-
API versioning, breaking changes, contract hashes, backward compatibility, what semver a
|
|
10
|
-
release should get, comparing against production/staging, or "pikku versions" / "pikku semver"
|
|
11
|
-
CLI commands. DO NOT TRIGGER when: user asks about secrets/variables/OAuth2 (use pikku-config)
|
|
12
|
-
or general function definitions (use pikku-concepts), or about updating dependency versions
|
|
13
|
-
(use pikku-deps).
|
|
14
|
-
---
|
|
15
|
-
|
|
16
1
|
# Pikku Function Versioning
|
|
17
2
|
|
|
18
|
-
## Agent Operating Procedure
|
|
19
|
-
|
|
20
|
-
Use this skill as an execution checklist, not reference material.
|
|
21
|
-
|
|
22
|
-
1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
|
|
23
|
-
2. Identify the source files that own the behavior. Do not start by reading generated output, `.pikku`, `node_modules`, vendored packages, or broad build artifacts.
|
|
24
|
-
3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
|
|
25
|
-
4. Validate with the narrowest relevant command first, then run `pikku-verify` or `pikku all` when functions, wirings, schemas, or generated clients may have changed.
|
|
26
|
-
5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
|
|
27
|
-
|
|
28
|
-
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.
|
|
29
3
|
|
|
30
4
|
## Before You Start
|
|
31
5
|
|
|
@@ -6,8 +6,8 @@ description: >-
|
|
|
6
6
|
understanding middleware execution order and priority. TRIGGER when: user wants middleware on
|
|
7
7
|
some or all routes, machine-to-machine auth, tag-scoped cross-cutting concerns, global
|
|
8
8
|
interceptors, or middleware priority/order questions. DO NOT TRIGGER when: user asks about
|
|
9
|
-
permissions
|
|
10
|
-
|
|
9
|
+
permissions, sessions or auth strategies like authBearer/authCookie (use
|
|
10
|
+
pikku-auth), or deployment.
|
|
11
11
|
installGroups: [core]
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -223,7 +223,7 @@ export const reportSomething = pikkuFunc({
|
|
|
223
223
|
})
|
|
224
224
|
```
|
|
225
225
|
|
|
226
|
-
An unresolved token leaves the session unset and the function throws `MissingSessionError` — 401, for free. Declare the scope tree once with `defineScope` (see `pikku-
|
|
226
|
+
An unresolved token leaves the session unset and the function throws `MissingSessionError` — 401, for free. Declare the scope tree once with `defineScope` (see `pikku-auth`).
|
|
227
227
|
|
|
228
228
|
### It MUST be `addHTTPMiddleware`, never `addTagMiddleware`
|
|
229
229
|
|
|
@@ -255,13 +255,13 @@ Unlike tag middleware over `/rpc`, this works: `runScheduledTask` builds its wir
|
|
|
255
255
|
|
|
256
256
|
### The one sessionless exception: bootstrap
|
|
257
257
|
|
|
258
|
-
An endpoint that runs BEFORE the caller has an identity — registering a new host with a shared bootstrap key, a login, a device-code request — has no session to set. That one stays `pikkuSessionlessFunc` and declares its gate in `permissions` (see `pikku-
|
|
258
|
+
An endpoint that runs BEFORE the caller has an identity — registering a new host with a shared bootstrap key, a login, a device-code request — has no session to set. That one stays `pikkuSessionlessFunc` and declares its gate in `permissions` (see `pikku-auth`).
|
|
259
259
|
|
|
260
260
|
## Service-to-Service Bearer Auth (gate-only pattern)
|
|
261
261
|
|
|
262
262
|
Use this when the callee needs to know only THAT the caller is trusted, not WHICH caller it is. If it needs to know which, use the session pattern above.
|
|
263
263
|
|
|
264
|
-
A server that exposes RPCs only to a trusted caller (e.g. an API calling a machine-agent). Auth lives in a tag middleware — NOT in the function body. Authorization/permission checks belong in the `permissions` field (see `pikku-
|
|
264
|
+
A server that exposes RPCs only to a trusted caller (e.g. an API calling a machine-agent). Auth lives in a tag middleware — NOT in the function body. Authorization/permission checks belong in the `permissions` field (see `pikku-auth`), never inside `func`.
|
|
265
265
|
|
|
266
266
|
**On the server (the service being called):** tag the function, register a `pikkuMiddleware` that reads the `Authorization` header on that tag.
|
|
267
267
|
|
|
@@ -3,7 +3,6 @@ name: pikku-n8n-import
|
|
|
3
3
|
description: 'Use to import an n8n workflow JSON export into a runnable Pikku workflow. Triggers when the user says "import this n8n workflow", "convert this n8n export to pikku", points at an n8n `.json` export or a directory of them, or picks up after `pikku import n8n` left throwing stub functions (`STUB — generated from n8n …`, `— implement me`) or a `<workflow>.integrations.json` manifest. Owns the whole flow: run the importer, triage what it could not map, fill each stub, report any missing `@pikku/addon-*` integrations, and verify the result compiles and runs with no surviving stubs. DO NOT TRIGGER for hand-written addon wiring unrelated to an n8n import (use pikku-addon), or for authoring workflows from scratch (use pikku-workflow).'
|
|
4
4
|
metadata:
|
|
5
5
|
version: 1.0.0
|
|
6
|
-
installGroups: [fabric]
|
|
7
6
|
---
|
|
8
7
|
|
|
9
8
|
# n8n → Pikku Import
|