@pikku/cli 0.12.82 → 0.12.83

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.
Files changed (214) hide show
  1. package/cli.schema.json +1 -1
  2. package/console-app/assets/{index-ChcXIEhS.js → index-CN_U0k_D.js} +154 -144
  3. package/console-app/assets/{index-DwyRdRuZ.css → index-CpHRlBt-.css} +1 -1
  4. package/console-app/index.html +2 -2
  5. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +3 -2
  6. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  7. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  8. package/dist/.pikku/cli/pikku-cli-channel.js +21 -1
  9. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  10. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  11. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  12. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  13. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  14. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  15. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  16. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +60 -3
  17. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  18. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  19. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  20. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  21. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  22. package/dist/.pikku/function/pikku-function-types.gen.d.ts +6 -12
  23. package/dist/.pikku/function/pikku-function-types.gen.js +3 -12
  24. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  25. package/dist/.pikku/function/pikku-functions-meta.gen.json +190 -12
  26. package/dist/.pikku/function/pikku-functions.gen.js +15 -1
  27. package/dist/.pikku/http/pikku-http-types.gen.d.ts +2 -24
  28. package/dist/.pikku/http/pikku-http-types.gen.js +2 -24
  29. package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
  30. package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
  31. package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
  32. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  33. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  34. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  35. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  36. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  37. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  38. package/dist/.pikku/pikku-services.gen.d.ts +4 -1
  39. package/dist/.pikku/pikku-services.gen.js +3 -0
  40. package/dist/.pikku/pikku-types.gen.d.ts +2 -1
  41. package/dist/.pikku/pikku-types.gen.js +3 -1
  42. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  43. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  44. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  45. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  46. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  47. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  48. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +9 -0
  49. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  50. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  51. package/dist/.pikku/schemas/register.gen.js +13 -1
  52. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  53. package/dist/.pikku/schemas/schemas/PikkuGraphWiringsOutput.schema.json +1 -0
  54. package/dist/.pikku/schemas/schemas/PikkuImportN8nInput.schema.json +1 -0
  55. package/dist/.pikku/schemas/schemas/PikkuScopesInput.schema.json +1 -0
  56. package/dist/.pikku/schemas/schemas/PikkuWebhookOutput.schema.json +1 -0
  57. package/dist/.pikku/schemas/schemas/ScopesAuditInput.schema.json +1 -0
  58. package/dist/.pikku/schemas/schemas/ScopesPruneInput.schema.json +1 -0
  59. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +5 -0
  60. package/dist/.pikku/scopes/pikku-scope-types.gen.js +4 -0
  61. package/dist/.pikku/scopes/pikku-scopes-meta.gen.json +1 -0
  62. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +35 -0
  63. package/dist/.pikku/scopes/pikku-scopes.gen.js +27 -0
  64. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  65. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  66. package/dist/.pikku/secrets/pikku-secrets-meta.gen.json +1 -0
  67. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +11 -1
  68. package/dist/.pikku/secrets/pikku-secrets.gen.js +11 -1
  69. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  70. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  71. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  72. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  73. package/dist/.pikku/variables/pikku-variables-meta.gen.json +1 -0
  74. package/dist/.pikku/variables/pikku-variables.gen.d.ts +11 -1
  75. package/dist/.pikku/variables/pikku-variables.gen.js +13 -3
  76. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +48 -6
  77. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +4 -1
  78. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  79. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  80. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  81. package/dist/bin/pikku-bin.mjs +2 -2
  82. package/dist/src/cli.wiring.js +47 -4
  83. package/dist/src/deploy/analyzer/analyzer.js +6 -0
  84. package/dist/src/fabric/fabric-commands.d.ts +3 -3
  85. package/dist/src/fabric/functions/llm-key.function.d.ts +4 -4
  86. package/dist/src/functions/commands/bootstrap.js +3 -0
  87. package/dist/src/functions/commands/dev.js +14 -3
  88. package/dist/src/functions/commands/enable.d.ts +1 -0
  89. package/dist/src/functions/commands/enable.js +10 -5
  90. package/dist/src/functions/commands/import-n8n.d.ts +10 -0
  91. package/dist/src/functions/commands/import-n8n.js +130 -0
  92. package/dist/src/functions/commands/info.js +0 -13
  93. package/dist/src/functions/commands/new-addon.js +33 -35
  94. package/dist/src/functions/commands/pikku-command-bootstrap.js +6 -0
  95. package/dist/src/functions/commands/scopes-audit.d.ts +1 -0
  96. package/dist/src/functions/commands/scopes-audit.js +37 -0
  97. package/dist/src/functions/commands/scopes-prune.d.ts +14 -0
  98. package/dist/src/functions/commands/scopes-prune.js +50 -0
  99. package/dist/src/functions/commands/scopes-shared.d.ts +39 -0
  100. package/dist/src/functions/commands/scopes-shared.js +83 -0
  101. package/dist/src/functions/commands/serve.js +4 -2
  102. package/dist/src/functions/commands/skills.js +18 -10
  103. package/dist/src/functions/wirings/ai-agent/serialize-ai-agent-types.js +2 -1
  104. package/dist/src/functions/wirings/console/serialize-console-functions.js +6 -0
  105. package/dist/src/functions/wirings/credentials/serialize-credentials-types.js +22 -0
  106. package/dist/src/functions/wirings/functions/pikku-command-function-types-split.js +1 -1
  107. package/dist/src/functions/wirings/functions/pikku-command-function-types.js +2 -2
  108. package/dist/src/functions/wirings/functions/serialize-function-types.d.ts +1 -1
  109. package/dist/src/functions/wirings/functions/serialize-function-types.js +14 -16
  110. package/dist/src/functions/wirings/functions/serialize-pikku-types-hub.d.ts +1 -1
  111. package/dist/src/functions/wirings/functions/serialize-pikku-types-hub.js +6 -2
  112. package/dist/src/functions/wirings/graph/pikku-command-graph-wirings.d.ts +1 -0
  113. package/dist/src/functions/wirings/graph/pikku-command-graph-wirings.js +21 -0
  114. package/dist/src/functions/wirings/graph/serialize-graph-wirings.d.ts +5 -0
  115. package/dist/src/functions/wirings/graph/serialize-graph-wirings.js +14 -0
  116. package/dist/src/functions/wirings/http/serialize-http-types.js +2 -30
  117. package/dist/src/functions/wirings/package/pikku-command-package-types.d.ts +1 -0
  118. package/dist/src/functions/wirings/package/pikku-command-package-types.js +14 -1
  119. package/dist/src/functions/wirings/package/serialize-package-types.d.ts +1 -0
  120. package/dist/src/functions/wirings/package/serialize-package-types.js +5 -0
  121. package/dist/src/functions/wirings/permissions/pikku-command-permissions.js +1 -3
  122. package/dist/src/functions/wirings/permissions/serialize-permissions-imports.d.ts +6 -1
  123. package/dist/src/functions/wirings/permissions/serialize-permissions-imports.js +7 -64
  124. package/dist/src/functions/wirings/rpc/pikku-command-rpc-map.d.ts +5 -0
  125. package/dist/src/functions/wirings/rpc/pikku-command-rpc-map.js +20 -0
  126. package/dist/src/functions/wirings/rpc/serialize-typed-rpc-map.d.ts +1 -0
  127. package/dist/src/functions/wirings/rpc/serialize-typed-rpc-map.js +8 -3
  128. package/dist/src/functions/wirings/scopes/pikku-command-scopes.d.ts +7 -0
  129. package/dist/src/functions/wirings/scopes/pikku-command-scopes.js +34 -0
  130. package/dist/src/functions/wirings/scopes/serialize-scopes-types.d.ts +15 -0
  131. package/dist/src/functions/wirings/scopes/serialize-scopes-types.js +86 -0
  132. package/dist/src/functions/wirings/secrets/serialize-secrets-types.js +13 -0
  133. package/dist/src/functions/wirings/variables/serialize-variables-types.js +15 -2
  134. package/dist/src/functions/wirings/webhook/pikku-command-webhook.d.ts +1 -0
  135. package/dist/src/functions/wirings/webhook/pikku-command-webhook.js +25 -0
  136. package/dist/src/functions/wirings/webhook/serialize-webhook.d.ts +14 -0
  137. package/dist/src/functions/wirings/webhook/serialize-webhook.js +34 -0
  138. package/dist/src/functions/wirings/workflow/serialize-workflow-routes.js +29 -0
  139. package/dist/src/functions/wirings/workflow/serialize-workflow-types.js +3 -0
  140. package/dist/src/functions/workflows/all.workflow.js +23 -3
  141. package/dist/src/services.d.ts +1 -0
  142. package/dist/src/services.js +1 -0
  143. package/dist/src/utils/pikku-cli-config.js +20 -0
  144. package/dist/tsconfig.tsbuildinfo +1 -1
  145. package/package.json +9 -8
  146. package/skills/pikku-addon/SKILL.md +7 -3
  147. package/skills/pikku-ai-agent/SKILL.md +6 -3
  148. package/skills/pikku-ai-vercel/SKILL.md +6 -3
  149. package/skills/pikku-ai-voice/SKILL.md +6 -3
  150. package/skills/pikku-audit/SKILL.md +10 -3
  151. package/skills/pikku-aws/SKILL.md +9 -5
  152. package/skills/pikku-backblaze/SKILL.md +4 -3
  153. package/skills/pikku-better-auth/SKILL.md +10 -5
  154. package/skills/pikku-cli/SKILL.md +6 -3
  155. package/skills/pikku-concepts/SKILL.md +9 -4
  156. package/skills/pikku-concepts/references/concept-mapping.md +12 -10
  157. package/skills/pikku-config/SKILL.md +9 -5
  158. package/skills/pikku-cron/SKILL.md +6 -3
  159. package/skills/pikku-deploy-azure/SKILL.md +5 -3
  160. package/skills/pikku-deploy-cloudflare/SKILL.md +5 -3
  161. package/skills/pikku-deploy-express/SKILL.md +6 -3
  162. package/skills/pikku-deploy-fastify/SKILL.md +6 -3
  163. package/skills/pikku-deploy-lambda/SKILL.md +6 -3
  164. package/skills/pikku-deploy-nextjs/SKILL.md +5 -3
  165. package/skills/pikku-deploy-uws/SKILL.md +7 -4
  166. package/skills/pikku-deps/SKILL.md +13 -6
  167. package/skills/pikku-emails/SKILL.md +10 -5
  168. package/skills/pikku-feature/SKILL.md +1 -1
  169. package/skills/pikku-gateway-slack/SKILL.md +6 -3
  170. package/skills/pikku-http/SKILL.md +15 -21
  171. package/skills/pikku-http/references/http-options.md +0 -2
  172. package/skills/pikku-info/SKILL.md +9 -4
  173. package/skills/pikku-jose/SKILL.md +6 -3
  174. package/skills/pikku-kysely/SKILL.md +13 -5
  175. package/skills/pikku-machine-auth/SKILL.md +9 -3
  176. package/skills/pikku-mcp/SKILL.md +7 -3
  177. package/skills/pikku-middleware/SKILL.md +11 -6
  178. package/skills/pikku-mongodb/SKILL.md +8 -5
  179. package/skills/pikku-n8n-import/SKILL.md +109 -0
  180. package/skills/pikku-n8n-import/SPEC.md +84 -0
  181. package/skills/pikku-n8n-import/references/addon-mapping.md +121 -0
  182. package/skills/pikku-n8n-import/references/code-translation.md +121 -0
  183. package/skills/pikku-n8n-import/references/loops-and-control.md +87 -0
  184. package/skills/pikku-permissions/SKILL.md +26 -15
  185. package/skills/pikku-pino/SKILL.md +5 -3
  186. package/skills/pikku-product-second-opinion/README.md +43 -0
  187. package/skills/pikku-product-second-opinion/SKILL.md +158 -0
  188. package/skills/pikku-product-second-opinion/example/sample-report.md +90 -0
  189. package/skills/pikku-product-second-opinion/references/report-template.md +73 -0
  190. package/skills/pikku-queue/SKILL.md +7 -4
  191. package/skills/pikku-redis/SKILL.md +9 -6
  192. package/skills/pikku-rpc/SKILL.md +7 -4
  193. package/skills/pikku-rtl/SKILL.md +1 -1
  194. package/skills/pikku-scenario/SKILL.md +215 -0
  195. package/skills/pikku-schedule/SKILL.md +6 -3
  196. package/skills/pikku-schema-ajv/SKILL.md +5 -3
  197. package/skills/pikku-schema-cfworker/SKILL.md +6 -3
  198. package/skills/pikku-security/SKILL.md +8 -4
  199. package/skills/pikku-services/SKILL.md +8 -4
  200. package/skills/pikku-software-archaeology/README.md +70 -0
  201. package/skills/pikku-software-archaeology/SKILL.md +186 -0
  202. package/skills/pikku-software-archaeology/references/blueprint.schema.json +625 -0
  203. package/skills/pikku-software-archaeology/references/pikku-mapping.md +49 -0
  204. package/skills/pikku-software-archaeology/scripts/validate.mjs +173 -0
  205. package/skills/pikku-template-clone/SKILL.md +1 -1
  206. package/skills/pikku-trigger/SKILL.md +7 -3
  207. package/skills/pikku-versioning/SKILL.md +7 -3
  208. package/skills/pikku-websocket/SKILL.md +8 -4
  209. package/skills/pikku-workflow/SKILL.md +7 -3
  210. package/skills/pikku-ws/SKILL.md +5 -3
  211. package/skills/pikku-n8n-addon-map/SKILL.md +0 -178
  212. package/skills/pikku-n8n-code-translate/SKILL.md +0 -166
  213. package/skills/pikku-testing/SKILL.md +0 -328
  214. package/skills/pikku-testing/references/cucumber-bdd-testing.md +0 -176
@@ -0,0 +1,49 @@
1
+ # How Pikku Consumes a Product Blueprint
2
+
3
+ The `.knowledge/` blueprint is designed so each concept maps onto exactly one Pikku primitive. A generator (or an agent following `pikku-feature`) walks the JSON files in this order:
4
+
5
+ | Blueprint source | Pikku target |
6
+ |---|---|
7
+ | `entities.json` attributes + relationships + constraints | Kysely migrations + generated `DB` types; Zod schemas per entity |
8
+ | `entities.json` states/transitions | a `status` column + transition guards inside the owning commands (or a state-machine helper) |
9
+ | `commands.json` | `pikkuFunc` / `pikkuSessionlessFunc` with `input:` Zod schema built from `input[]`; `preconditions` become guard clauses; name is the camelCased command name (`SendInvoice` → `sendInvoice`) |
10
+ | `queries.json` | `pikkuFunc` reads; `scoping` becomes the mandatory `WHERE` / session filter |
11
+ | `events.json` | EventHub topics (realtime) or queue messages; `consumedBy` become `wireQueueWorker` handlers — implicit events (`explicit: false`) get promoted to real emissions |
12
+ | `policies.json` (authorization) | Pikku `permissions` / middleware; one policy = one named permission function, wired everywhere `enforcedAt` listed — this collapses duplicated legacy checks into a single definition |
13
+ | `policies.json` (validation) | Zod schema refinements on the command's `input` |
14
+ | `workflows.json` kind=user | frontend flows + the commands they chain |
15
+ | `workflows.json` kind=system, with `schedule` | `wireScheduler` entries |
16
+ | `workflows.json` multi-step / checkpointing | `pikkuWorkflowFunc` with one `workflow.do(...)` step per blueprint step |
17
+ | `workflows.json` `scenarios[]` | **`pikkuUserFlow` stories — this is the canonical target.** Each scenario's given/when/outcome maps 1:1 onto a user-flow step sequence; group scenarios by their workflow into one flow per journey. Only scenarios with no user-facing surface (pure system workflows: cron sweeps, webhook ingest) fall back to API/e2e tests |
18
+ | `api.json` | `wireHTTP` routes: keep `path`+`method` for compatibility, point at the mapped command/query func; `auth: none`/capability-URL surfaces get `auth: false` |
19
+ | `api.json` kind=webhook-in | `wireHTTP` with `auth: false` + signature-verification middleware from the integration |
20
+ | `integrations.json` | services in `services.ts` (constructor-injected classes); `configVia` env vars become `wireSecret` / config; per-user credentials become `wireCredential` |
21
+ | `architecture.json` notes | deployment config (ports, raw-body routes, proxy expectations) |
22
+ | `invariants.json` enforcedBy=db-constraint | migration constraints (UNIQUE, CHECK, FK) |
23
+ | `invariants.json` enforcedBy=code-guard/nothing | guard clauses + a test each; `atRiskBecause` entries get a hardening task |
24
+ | `gaps.json` | excluded from generation; `open-product-decision` + `migration.json.decisionsNeeded` go to a human BEFORE generation starts |
25
+ | `migration.json.mappings` | the work plan: one mapping = one migration slice |
26
+ | `interfaces.json` kind=cli | `wireCLI` entrypoints — the CLI commands are the same funcs the routes expose |
27
+ | `interfaces.json` kind=mcp | `wireMCP` — each MCP tool IS a `pikkuFunc` (reuse the command/query funcs; don't author tool duplicates) |
28
+ | `interfaces.json` kind=openapi-rest / sdk | generated, not hand-written: the OpenAPI spec + typed client SDK fall out of the `wireHTTP` routes + codegen |
29
+ | `interfaces.json` kind=websocket-realtime | `pikku-realtime` EventHub topics / channels |
30
+ | `frontend.json` | `apps/app` (TanStack Start) shell: router, `@pikku/mantine` theme, `pikku-react-query` data layer, `better-auth` client — the target stack the legacy UI is rebuilt onto |
31
+ | `frontend-routes.json` | TanStack Router routes under `apps/app/src/routes/**` (thin data containers calling `usePikkuQuery`); `dataFrom` names become the generated hooks; subpath routes for rich detail views |
32
+ | `frontend-components.json` rebuild=`mantine-standard`/`mantine-composition` | components in `packages/components` composed from `@pikku/mantine` — the trivial/straightforward bulk |
33
+ | `frontend-components.json` rebuild=`custom-logic` | the PORT list — each becomes a `packages/components` component that reimplements the bespoke behavior (chart/table/editor); its `dependencies` inform whether the lib is kept or replaced. These are the frontend's real work items |
34
+ | `frontend-components.json` rebuild=`custom-style` | normalize to Mantine/theme tokens; usually deleted-and-recomposed, not ported |
35
+
36
+ ## Order of generation
37
+
38
+ 1. Human resolves `decisionsNeeded`.
39
+ 2. Entities → migrations + types.
40
+ 3. Policies → permission functions (before commands, so commands can reference them).
41
+ 4. Commands + queries → funcs; api.json → wirings.
42
+ 5. Events → topics/queues; system workflows → schedulers/workers/workflows.
43
+ 6. Scenarios → tests. Run them against the new implementation; they encode the legacy behavior worth preserving.
44
+
45
+ ## Uncertainty handling
46
+
47
+ - `confidence: high` concepts generate directly.
48
+ - `confidence: medium` concepts generate, but are listed for review in the pre-generation report — id, evidence summary, and what is uncertain — rather than carrying a marker comment in the generated code. The report is the review surface; the generated code stays clean.
49
+ - `confidence: low` concepts are NOT generated automatically — they surface in the pre-generation review along with `decisionsNeeded`.
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env node
2
+ // Validates a .knowledge/ blueprint directory against references/blueprint.schema.json,
3
+ // then runs cross-file referential checks (does every command's domain exist, does every
4
+ // api surface map to a real command/query, ...). Exit 0 = valid, 1 = errors.
5
+ //
6
+ // Usage: node validate.mjs <path-to-.knowledge-dir>
7
+
8
+ import { readFileSync, existsSync } from 'node:fs';
9
+ import { join, dirname } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ const here = dirname(fileURLToPath(import.meta.url));
13
+ const schemaDoc = JSON.parse(readFileSync(join(here, '..', 'references', 'blueprint.schema.json'), 'utf8'));
14
+
15
+ const dir = process.argv[2];
16
+ if (!dir) { console.error('usage: node validate.mjs <.knowledge dir>'); process.exit(2); }
17
+
18
+ const errors = [];
19
+ const warnings = [];
20
+
21
+ // --- minimal JSON-Schema-subset validator (type, required, properties, items, enum, minItems, pattern, $ref -> $defs) ---
22
+ function resolveRef(ref) {
23
+ const m = /^#\/\$defs\/(\w+)$/.exec(ref);
24
+ if (!m || !schemaDoc.$defs[m[1]]) throw new Error(`unresolvable $ref ${ref}`);
25
+ return schemaDoc.$defs[m[1]];
26
+ }
27
+
28
+ function check(value, schema, path) {
29
+ if (schema.$ref) schema = { ...resolveRef(schema.$ref), ...schema, $ref: undefined };
30
+ if (schema.enum && !schema.enum.includes(value)) {
31
+ errors.push(`${path}: expected one of [${schema.enum.join(', ')}], got ${JSON.stringify(value)}`);
32
+ return;
33
+ }
34
+ const t = schema.type;
35
+ if (t === 'object') {
36
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
37
+ errors.push(`${path}: expected object`); return;
38
+ }
39
+ for (const req of schema.required || []) {
40
+ if (!(req in value)) errors.push(`${path}: missing required field "${req}"`);
41
+ }
42
+ for (const [k, v] of Object.entries(value)) {
43
+ if (schema.properties?.[k]) check(v, schema.properties[k], `${path}.${k}`);
44
+ }
45
+ } else if (t === 'array') {
46
+ if (!Array.isArray(value)) { errors.push(`${path}: expected array`); return; }
47
+ if (schema.minItems && value.length < schema.minItems) {
48
+ errors.push(`${path}: needs at least ${schema.minItems} item(s), has ${value.length}`);
49
+ }
50
+ if (schema.items) value.forEach((v, i) => check(v, schema.items, `${path}[${i}]`));
51
+ } else if (t === 'string') {
52
+ if (typeof value !== 'string') { errors.push(`${path}: expected string`); return; }
53
+ if (schema.pattern && !new RegExp(schema.pattern).test(value)) {
54
+ errors.push(`${path}: "${value}" does not match ${schema.pattern}`);
55
+ }
56
+ } else if (t === 'boolean' && typeof value !== 'boolean') {
57
+ errors.push(`${path}: expected boolean`);
58
+ } else if (t === 'number' && typeof value !== 'number') {
59
+ errors.push(`${path}: expected number`);
60
+ }
61
+ }
62
+
63
+ // --- load + per-file validation ---
64
+ // Files marked `x-optional` (the frontend layer) only validate when present, so a
65
+ // backend-only repo does not fail for lacking them.
66
+ const docs = {};
67
+ for (const [filename, fileSchema] of Object.entries(schemaDoc.files)) {
68
+ const p = join(dir, filename);
69
+ if (!existsSync(p)) {
70
+ if (!fileSchema['x-optional']) errors.push(`${filename}: missing`);
71
+ continue;
72
+ }
73
+ try {
74
+ docs[filename] = JSON.parse(readFileSync(p, 'utf8'));
75
+ } catch (e) {
76
+ errors.push(`${filename}: invalid JSON (${e.message})`); continue;
77
+ }
78
+ check(docs[filename], fileSchema, filename);
79
+ }
80
+ if (!existsSync(join(dir, 'blueprint.md'))) errors.push('blueprint.md: missing');
81
+
82
+ // --- cross-file referential checks ---
83
+ if (docs['domains.json'] && docs['commands.json']) {
84
+ const domains = new Set((docs['domains.json'].domains || []).map((d) => d.name));
85
+ const commandNames = new Set((docs['commands.json'].commands || []).map((c) => c.name));
86
+ const queryNames = new Set((docs['queries.json']?.queries || []).map((q) => q.name));
87
+ const eventNames = new Set((docs['events.json']?.events || []).map((e) => e.name));
88
+
89
+ const wantDomain = (owner, d) => {
90
+ if (d && !domains.has(d)) errors.push(`${owner}: domain "${d}" not defined in domains.json`);
91
+ };
92
+ for (const c of docs['commands.json'].commands || []) {
93
+ wantDomain(`commands.json:${c.name}`, c.domain);
94
+ for (const ev of c.eventsProduced || []) {
95
+ if (!eventNames.has(ev)) warnings.push(`commands.json:${c.name} produces "${ev}" which is not in events.json`);
96
+ }
97
+ }
98
+ for (const q of docs['queries.json']?.queries || []) wantDomain(`queries.json:${q.name}`, q.domain);
99
+ for (const e of docs['entities.json']?.entities || []) wantDomain(`entities.json:${e.name}`, e.domain);
100
+ for (const ev of docs['events.json']?.events || []) wantDomain(`events.json:${ev.name}`, ev.domain);
101
+
102
+ for (const s of docs['api.json']?.surfaces || []) {
103
+ const { type, name } = s.mapsTo || {};
104
+ if (type === 'command' && !commandNames.has(name)) errors.push(`api.json:${s.method || ''} ${s.path}: maps to unknown command "${name}"`);
105
+ if (type === 'query' && !queryNames.has(name)) errors.push(`api.json:${s.method || ''} ${s.path}: maps to unknown query "${name}"`);
106
+ if (type === 'event-ingress' && !eventNames.has(name)) errors.push(`api.json:${s.method || ''} ${s.path}: event-ingress maps to unknown event "${name}" (state-changing webhooks should map to a command instead)`);
107
+ }
108
+ // every domain's listed concepts should exist
109
+ for (const d of docs['domains.json'].domains || []) {
110
+ for (const c of d.commands || []) if (!commandNames.has(c)) warnings.push(`domains.json:${d.name}: lists command "${c}" not in commands.json`);
111
+ for (const q of d.queries || []) if (!queryNames.has(q)) warnings.push(`domains.json:${d.name}: lists query "${q}" not in queries.json`);
112
+ for (const e of d.events || []) if (!eventNames.has(e)) warnings.push(`domains.json:${d.name}: lists event "${e}" not in events.json`);
113
+ const policyNames = new Set((docs['policies.json']?.policies || []).map((p) => p.name));
114
+ for (const p of d.policies || []) if (!policyNames.has(p)) warnings.push(`domains.json:${d.name}: lists policy "${p}" not in policies.json`);
115
+ }
116
+ // commands with no policies and no preconditions are suspicious for mutating ops
117
+ for (const c of docs['commands.json'].commands || []) {
118
+ if (!(c.policies || []).length && !(c.preconditions || []).length) {
119
+ warnings.push(`commands.json:${c.name}: no policies or preconditions — really unguarded, or missed extraction?`);
120
+ }
121
+ }
122
+ }
123
+
124
+ // --- frontend layer cross-checks (only when the optional frontend files exist) ---
125
+ if (docs['frontend-components.json']) {
126
+ const componentNames = new Set(
127
+ (docs['frontend-components.json'].components || []).map((c) => c.name),
128
+ );
129
+ // routes should reference components that were actually inventoried
130
+ for (const r of docs['frontend-routes.json']?.routes || []) {
131
+ for (const c of r.usesComponents || []) {
132
+ if (!componentNames.has(c)) {
133
+ warnings.push(`frontend-routes.json:${r.path}: uses component "${c}" not in frontend-components.json`);
134
+ }
135
+ }
136
+ }
137
+ // a component flagged as needing a port must say WHY (the custom logic), or the
138
+ // port-risk is unactionable
139
+ for (const c of docs['frontend-components.json'].components || []) {
140
+ if (c.rebuild === 'custom-logic' && !c.customLogic) {
141
+ warnings.push(`frontend-components.json:${c.name}: rebuild=custom-logic but no customLogic description — port risk is unactionable`);
142
+ }
143
+ }
144
+ // data-fetching queries named on routes should resolve to a real query/command
145
+ if (docs['queries.json'] || docs['commands.json']) {
146
+ const known = new Set([
147
+ ...(docs['queries.json']?.queries || []).map((q) => q.name),
148
+ ...(docs['commands.json']?.commands || []).map((c) => c.name),
149
+ ]);
150
+ for (const r of docs['frontend-routes.json']?.routes || []) {
151
+ for (const d of r.dataFrom || []) {
152
+ if (!known.has(d)) {
153
+ warnings.push(`frontend-routes.json:${r.path}: reads "${d}" which is not a known query/command`);
154
+ }
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ // an inconsistent UI with no specific design findings = under-extraction
161
+ // (guarded on frontend.json alone — independent of the component inventory)
162
+ if (docs['frontend.json']) {
163
+ const consistency = docs['frontend.json'].designSystemConsistency;
164
+ const findingCount = (docs['frontend.json'].designFindings || []).length;
165
+ if ((consistency === 'mixed' || consistency === 'ad-hoc') && findingCount === 0) {
166
+ warnings.push(`frontend.json: designSystemConsistency="${consistency}" but designFindings is empty — name the specific broken patterns (interaction/theming/cross-page/…)`);
167
+ }
168
+ }
169
+
170
+ for (const w of warnings) console.log(`WARN ${w}`);
171
+ for (const e of errors) console.log(`ERROR ${e}`);
172
+ console.log(`\n${errors.length} error(s), ${warnings.length} warning(s) across ${Object.keys(docs).length} files`);
173
+ process.exit(errors.length ? 1 : 0);
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: pikku-template-clone
3
- description: '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.'
3
+ description: '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 `npm create pikku`, `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.'
4
4
  allowed-tools: Bash(git status *), Bash(git add *), Bash(git commit *), Bash(git rm *), Bash(git mv *), Bash(git log *)
5
5
  ---
6
6
 
@@ -1,8 +1,12 @@
1
1
  ---
2
2
  name: pikku-trigger
3
- description: 'Use when adding event-driven functions that respond to system events like Redis pub/sub, PostgreSQL LISTEN/NOTIFY, or custom event sources. Covers wireTrigger, wireTriggerSource, and pikkuTriggerFunc.
4
- TRIGGER when: code uses wireTrigger/wireTriggerSource/pikkuTriggerFunc, user asks about event-driven functions, Redis pub/sub, PostgreSQL LISTEN/NOTIFY, or reacting to external events.
5
- DO NOT TRIGGER when: user asks about scheduled tasks (use pikku-cron) or background job queues (use pikku-queue).'
3
+ description: >-
4
+ Use when adding event-driven functions that respond to system events like Redis pub/sub,
5
+ PostgreSQL LISTEN/NOTIFY, or custom event sources. Covers wireTrigger, wireTriggerSource, and
6
+ pikkuTriggerFunc. TRIGGER when: code uses wireTrigger/wireTriggerSource/pikkuTriggerFunc, user
7
+ asks about event-driven functions, Redis pub/sub, PostgreSQL LISTEN/NOTIFY, or reacting to
8
+ external events. DO NOT TRIGGER when: user asks about scheduled tasks (use pikku-cron) or
9
+ background job queues (use pikku-queue).
6
10
  installGroups: [core]
7
11
  ---
8
12
 
@@ -1,8 +1,12 @@
1
1
  ---
2
2
  name: pikku-versioning
3
- description: '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.
4
- TRIGGER when: code uses version: on a pikkuFunc, user asks about API versioning, breaking changes, contract hashes, backward compatibility, or "pikku versions" CLI commands.
5
- DO NOT TRIGGER when: user asks about secrets/variables/OAuth2 (use pikku-config) or general function definitions (use pikku-concepts).'
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. TRIGGER when: code uses version: on a pikkuFunc, user asks about
7
+ API versioning, breaking changes, contract hashes, backward compatibility, or "pikku versions"
8
+ CLI commands. DO NOT TRIGGER when: user asks about secrets/variables/OAuth2 (use pikku-config)
9
+ or general function definitions (use pikku-concepts).
6
10
  installGroups: [core]
7
11
  ---
8
12
 
@@ -1,8 +1,12 @@
1
1
  ---
2
2
  name: pikku-websocket
3
- description: 'Use when adding real-time features, WebSocket channels, live updates, chat, or pub/sub to a Pikku app. Covers wireChannel, action routing, auth, EventHub pub/sub, channel middleware, and generated WebSocket client.
4
- TRIGGER when: code uses wireChannel, user asks about WebSocket, real-time, live updates, chat, pub/sub, or the generated WebSocket client.
5
- DO NOT TRIGGER when: user asks about HTTP/REST (use pikku-http), SSE (use pikku-http with sse: true), or WebSocket deployment specifics (use pikku-deploy-uws).'
3
+ description: >-
4
+ Use when adding real-time features, WebSocket channels, live updates, chat, or pub/sub to a
5
+ Pikku app. Covers wireChannel, action routing, auth, EventHub pub/sub, channel middleware, and
6
+ generated WebSocket client. TRIGGER when: code uses wireChannel, user asks about WebSocket,
7
+ real-time, live updates, chat, pub/sub, or the generated WebSocket client. DO NOT TRIGGER when:
8
+ user asks about HTTP/REST (use pikku-http), SSE (use pikku-http with sse: true), or WebSocket
9
+ deployment specifics (use pikku-deploy-uws).
6
10
  ---
7
11
 
8
12
  # Pikku WebSocket Wiring
@@ -172,7 +176,7 @@ wireChannel({
172
176
 
173
177
  ### Generated WebSocket Client
174
178
 
175
- After `npx pikku prebuild`:
179
+ After `npx pikku all`:
176
180
 
177
181
  ```typescript
178
182
  import { PikkuWebSocket } from '.pikku/pikku-websocket.gen.js'
@@ -1,8 +1,12 @@
1
1
  ---
2
2
  name: pikku-workflow
3
- description: 'Use when building multi-step workflows, state machines, or orchestration pipelines with Pikku. Covers pikkuWorkflowFunc, workflow steps (do, sleep, suspend), graph workflows, and HTTP wiring.
4
- TRIGGER when: code uses pikkuWorkflowFunc/pikkuWorkflowGraph, user asks about workflows, multi-step processes, durable execution, suspend/resume, or DAG orchestration.
5
- DO NOT TRIGGER when: user asks about simple background jobs (use pikku-queue) or scheduled tasks (use pikku-cron).'
3
+ description: >-
4
+ Use when building multi-step workflows, state machines, or orchestration pipelines with Pikku.
5
+ Covers pikkuWorkflowFunc, workflow steps (do, sleep, suspend), graph workflows, and HTTP wiring.
6
+ TRIGGER when: code uses pikkuWorkflowFunc/pikkuWorkflowGraph, user asks about workflows,
7
+ multi-step processes, durable execution, suspend/resume, or DAG orchestration. DO NOT TRIGGER
8
+ when: user asks about simple background jobs (use pikku-queue) or scheduled tasks (use
9
+ pikku-cron).
6
10
  installGroups: [core]
7
11
  ---
8
12
 
@@ -1,8 +1,10 @@
1
1
  ---
2
2
  name: pikku-ws
3
- description: 'Use when setting up a WebSocket server with the ws library in a Pikku app. Covers the ws runtime adapter for Pikku channels.
4
- TRIGGER when: code uses @pikku/ws, user asks about ws library WebSocket server, or Node.js WebSocket runtime.
5
- DO NOT TRIGGER when: user asks about WebSocket wiring/channels (use pikku-websocket) or uWebSockets (use pikku-deploy-uws).'
3
+ description: >-
4
+ Use when setting up a WebSocket server with the ws library in a Pikku app. Covers the ws runtime
5
+ adapter for Pikku channels. TRIGGER when: code uses @pikku/ws, user asks about ws library
6
+ WebSocket server, or Node.js WebSocket runtime. DO NOT TRIGGER when: user asks about WebSocket
7
+ wiring/channels (use pikku-websocket) or uWebSockets (use pikku-deploy-uws).
6
8
  ---
7
9
 
8
10
  # Pikku WS (WebSocket Server Runtime)
@@ -1,178 +0,0 @@
1
- ---
2
- name: pikku-n8n-addon-map
3
- description: 'Use when mapping n8n integration stubs (gmailTool, slackTool, googleSheetsTool, plain gmail/slack action nodes, etc.) emitted by @pikku/n8n-import to real `@pikku/addon-*` functions. Triggered when the user points at a `<workflow>.integrations.json` manifest produced by `pikku-n8n-import`, says ''map the n8n integrations'', ''wire up the gmail/slack stubs'', ''replace these stubs with addon refs'', or opens a stub file generated from an n8n integration node (the stub''s JSDoc says `STUB — generated from n8n node "..." (type "n8n-nodes-base.<service>...")`). For n8n **Code** node stubs use `pikku-n8n-code-translate` instead. DO NOT TRIGGER when: the stub is an n8n Code node (use pikku-n8n-code-translate), or no n8n-generated stub/manifest is involved (use pikku-addon for hand-written addon wiring).'
4
- metadata:
5
- version: 1.0.0
6
- ---
7
-
8
- # n8n Integration Stub → Pikku Addon Mapper
9
-
10
- ## Agent Operating Procedure
11
-
12
- Use this skill as an execution checklist, not reference material.
13
-
14
- 1. Discover before editing. Prefer OpenCode tools such as `pikku-meta` when available; otherwise run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
15
- 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.
16
- 3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
17
- 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.
18
- 5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
19
-
20
- You are translating n8n integration nodes (`gmailTool`, `slackTool`, `googleSheetsTool`, plain `gmail` / `slack` action nodes, etc.) that `@pikku/n8n-import` left as throwing stubs into real `ref('<addonRpc>')` references that point at functions in installed `@pikku/addon-*` packages.
21
-
22
- This skill is **per-stub mechanical**. You do not invent business logic, you do not chain calls, you do not "improve" the workflow. You read one entry from a manifest, find the matching addon function, and rewrite the stub.
23
-
24
- ## Inputs
25
-
26
- 1. **Manifest file** — `<workflow>.integrations.json` next to the workflow `.graph.ts`. Each entry:
27
- ```jsonc
28
- {
29
- "rpcName": "agentGmailtool__sendAMessageInGmail",
30
- "n8nType": "n8n-nodes-base.gmailTool",
31
- "n8nName": "Send a message in Gmail",
32
- "parameters": { "sendTo": "...", "message": "...", "subject": "..." },
33
- "credentials": {
34
- "gmailOAuth2": { "id": "...", "name": "Personal Gmail" },
35
- },
36
- "isAgentTool": true,
37
- "agentName": "Inbox Assistant",
38
- }
39
- ```
40
- 2. **Installed addons** — anything matching `@pikku/addon-*` in the project's `package.json` `dependencies`. The mapping is _only_ allowed against installed packages. If the addon for a given n8n type is not installed, surface that — do not silently skip and do not pick a vaguely-named function from another addon.
41
-
42
- ## What you actually do, in order
43
-
44
- For **each** entry in the manifest:
45
-
46
- ### Step 1 — identify the target addon
47
-
48
- Map `n8nType` to a `@pikku/addon-*` package by reading its source. Common shapes:
49
-
50
- | n8n type prefix | typical addon package candidate(s) |
51
- | ------------------------------------------ | ---------------------------------- |
52
- | `n8n-nodes-base.gmail` / `gmailTool` | `@pikku/addon-email-gmail` |
53
- | `n8n-nodes-base.slack` / `slackTool` | `@pikku/addon-chat-slack` |
54
- | `n8n-nodes-base.googleSheets` / `…Tool` | `@pikku/addon-sheets-google` |
55
- | `n8n-nodes-base.notion` / `notionTool` | `@pikku/addon-docs-notion` |
56
- | `n8n-nodes-base.telegram` / `telegramTool` | `@pikku/addon-chat-telegram` |
57
-
58
- **These are guesses, not authoritative.** Always verify by reading the installed addon's `src/functions/**` to confirm the exported function names exist. If you cannot find an installed addon that plausibly covers this n8n type, stop and tell the user — do not pick a wrong addon.
59
-
60
- ### Step 2 — pick the function (resource + operation → fn name)
61
-
62
- n8n integration nodes use a `(resource, operation)` pair to disambiguate. The mapping rubric:
63
-
64
- - `resource` defaults to the integration's primary noun if absent (gmail → `message`, slack → `message`, sheets → `spreadsheet`, etc.). Look at the addon's folder structure (`messages/`, `drafts/`, `channels/`) to see what nouns exist.
65
- - `operation` is usually a verb (`get`, `getAll`, `send`, `delete`, `addLabels`, `markAsRead`, `create`).
66
- - The pikku addon function name is almost always `<resource><Verb>` in camelCase, matching the file's `export const` (e.g. `messageList` for `messages/list.function.ts`, `draftCreate` for `drafts/create.function.ts`).
67
-
68
- Verify by `grep -h "^export const" <addonPkg>/src/functions/**/*.ts` and matching by name.
69
-
70
- Conventions seen in `@pikku/addon-email-gmail` (use as a sanity reference, **not** as a fallback if the addon is something else):
71
-
72
- - `getAll` → `<resource>List`
73
- - `get` → `<resource>Get`
74
- - `send` → `<resource>Send`
75
- - `delete` → `<resource>Delete`
76
- - `reply` → `<resource>Reply`
77
- - `addLabels` → `<resource>AddLabel` (singular!)
78
- - `removeLabels` → `<resource>RemoveLabel`
79
- - `markAsRead` / `markAsUnread` → `<resource>MarkRead` / `<resource>MarkUnread`
80
- - `create` (drafts/labels) → `<resource>Create`
81
-
82
- If the addon has a `node:` block on the function, prefer matching on that block's `category`/`displayName` over guessing — read the source.
83
-
84
- ### Step 3 — rewrite the stub
85
-
86
- The stub file currently looks like:
87
-
88
- ```ts
89
- import { z } from 'zod'
90
- import { pikkuSessionlessFunc } from '#pikku'
91
-
92
- export const AgentGmailtoolSendAMessageInGmailInput = z.object({
93
- items: z.array(z.unknown()).optional(),
94
- })
95
- export const AgentGmailtoolSendAMessageInGmailOutput = z.object({
96
- items: z.array(z.unknown()),
97
- })
98
-
99
- /** STUB — generated from n8n node "Send a message in Gmail" (type "n8n-nodes-base.gmailTool"). … */
100
- export const agentGmailtool__sendAMessageInGmail = pikkuSessionlessFunc({
101
- func: async () => {
102
- throw new Error('agentGmailtool__sendAMessageInGmail — implement me')
103
- },
104
- })
105
- ```
106
-
107
- **There are two outcomes**, depending on `isAgentTool`:
108
-
109
- #### A) `isAgentTool: true` — the stub is consumed by an agent via `ref()`
110
-
111
- The agent file references the stub by its export name. The cleanest path is:
112
-
113
- 1. **Delete the stub file entirely.**
114
- 2. In the agent file (look in the same emitted directory or `src/functions`), update the agent's `tools: [...]` array — replace `ref('agentGmailtool__sendAMessageInGmail')` with `ref('messageSend')` (or whichever addon function name you resolved).
115
- 3. Make sure the addon package is imported wherever pikku scans functions (typically already handled by pikku CLI scanning `node_modules/@pikku/addon-*`).
116
-
117
- If you cannot delete the stub safely (e.g. it has multiple consumers, or the user wants to keep a thin wrapper for renaming), leave a _one-line_ re-export wrapper instead:
118
-
119
- ```ts
120
- import { messageSend } from '@pikku/addon-email-gmail'
121
- export const agentGmailtool__sendAMessageInGmail = messageSend
122
- ```
123
-
124
- But the default is delete + retarget. Wrappers add maintenance burden.
125
-
126
- #### B) `isAgentTool: false` — the stub is part of the workflow graph proper
127
-
128
- 1. Open the workflow `.graph.ts` file.
129
- 2. Find the entry in `nodes: { … }` whose value is the stub's rpc name (e.g. `'agentGmailtool__sendAMessageInGmail'`).
130
- 3. Replace the value with the addon function name (e.g. `'messageSend'`).
131
- 4. If the workflow's `config: { <id>: { input: … } }` block has an `input` expression that produces an `{ items }` envelope, rewrite it to produce the addon's actual input schema (read the addon function's `input: z.object({...})` to know the shape).
132
- 5. Delete the stub file.
133
-
134
- ### Step 4 — port hard-coded parameters
135
-
136
- n8n parameters fall into two camps:
137
-
138
- - **Hardcoded values** (e.g. `"limit": 20`, `"labelIds": ["INBOX"]`, `"sendTo": "alice@example.com"`) — these were user choices in the n8n UI. Preserve them in the workflow's `input` expression (case B), or, for agent tools (case A), document them in the agent's tool list comment so the user knows what was lost. **Agent tools cannot carry hardcoded params** — the LLM fills the args at call time. If the user _needs_ a hardcoded value baked in, they must keep a wrapper function. Surface this trade-off explicitly.
139
- - **`$fromAI('Name', '', 'string')` placeholders** — these are LLM-filled. They map naturally to addon function input fields the LLM will populate via the `pikkuAIAgent`'s tool-calling. No action needed beyond deleting the placeholder string; the addon's Zod schema becomes the tool schema.
140
-
141
- ### Step 5 — credentials
142
-
143
- Each entry's `credentials: { gmailOAuth2: { id, name } }` is the n8n credential reference. Pikku addons typically expect a service (e.g. `services.gmail`) wired in `services.ts`. Do **not** attempt to auto-wire — append a one-line note for the user:
144
-
145
- > `// TODO: wire services.gmail using credential "Personal Gmail" (n8n id: gmail_cred_1) — see @pikku/addon-email-gmail/README.md`
146
-
147
- …either at the top of the workflow file or printed in your final summary.
148
-
149
- ## What you must NOT do
150
-
151
- - **Do not invent functions** in addon packages that don't exist. Grep first.
152
- - **Do not pick the wrong addon** because the right one isn't installed. Stop and tell the user `npm i @pikku/addon-<x>` is required.
153
- - **Do not bake per-mapping tables into `@pikku/n8n-import`.** That package is intentionally addon-agnostic. All mapping logic lives here in this skill.
154
- - **Do not modify the manifest file.** It's an audit artifact. Leave it alone.
155
- - **Do not chain calls** ("send and then mark as read"). Each manifest entry maps to _one_ addon function. If the n8n graph composed multiple steps, the n8n-import already represented that as multiple stubs / multiple workflow nodes.
156
- - **Do not silently drop hardcoded params.** If the addon function has no place to put a value, surface it in the summary.
157
-
158
- ## After you finish
159
-
160
- Print a short summary to the user:
161
-
162
- ```
163
- Mapped 3 of 5 stubs:
164
- ✓ agentGmailtool__sendAMessageInGmail → ref('messageSend') [agent tool: Inbox Assistant]
165
- ✓ agentGmailtool__getManyMessagesInGmail → ref('messageList') [agent tool: Inbox Assistant]
166
- ✓ workflow node 'updateRow' → 'sheetRowAppend' [graph node]
167
-
168
- Unmapped:
169
- ✗ slackTool 'Post to channel' — @pikku/addon-chat-slack not installed (npm i @pikku/addon-chat-slack)
170
- ✗ notionTool 'Create page' — no `pageCreate` in @pikku/addon-docs-notion (only `pageGet`, `pageUpdate`)
171
-
172
- Hardcoded params worth knowing:
173
- • Get many messages in Gmail had limit=20, labelIds=["INBOX"] — agent tools cannot pin these; if you need them fixed, add a thin wrapper function.
174
-
175
- Credential wiring TODOs added to the top of agentGmailtool.graph.ts.
176
- ```
177
-
178
- Be terse. The user already knows the workflow context — they pointed you at the manifest.