@pikku/skills 0.12.11 → 0.12.13
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 +74 -0
- package/dist/skills.gen.js +1 -1
- package/package.json +1 -1
- package/skills/pikku-addon/references/addon-package-manifest.md +61 -11
- package/skills/pikku-agent/SKILL.md +1 -2
- package/skills/pikku-emails/SKILL.md +23 -2
- package/skills/pikku-realtime/SKILL.md +1 -2
- package/skills/pikku-rpc/SKILL.md +4 -2
- package/skills/pikku-scenario/SKILL.md +5 -6
package/package.json
CHANGED
|
@@ -6,18 +6,24 @@
|
|
|
6
6
|
|
|
7
7
|
```text
|
|
8
8
|
my-addon/
|
|
9
|
-
├── package.json #
|
|
9
|
+
├── package.json # imports -> dist, exports -> dist
|
|
10
10
|
├── pikku.config.json # addon: true + metadata
|
|
11
|
-
├── tsconfig.json # #pikku path mapping
|
|
11
|
+
├── tsconfig.json # #pikku path mapping (source side)
|
|
12
12
|
├── src/
|
|
13
13
|
│ ├── services.ts # createSingletonServices (required)
|
|
14
14
|
│ └── functions/
|
|
15
15
|
│ └── *.function.ts # Function definitions
|
|
16
16
|
├── types/
|
|
17
17
|
│ └── application-types.d.ts # SingletonServices interface
|
|
18
|
-
└── .pikku/
|
|
18
|
+
└── .pikku/addon/ # Generated (gitignored)
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
An addon's generated tree roots one level down, at `.pikku/addon/`, so its own
|
|
22
|
+
leaves are reached as `#pikku/addon/<leaf>` while an application's are
|
|
23
|
+
`#pikku/<leaf>`. `paths` are global to a tsx process rather than scoped to the
|
|
24
|
+
package that declared them, and the extra segment is what stops a linked addon's
|
|
25
|
+
`#pikku/function` from matching the *host application's* flat leaf.
|
|
26
|
+
|
|
21
27
|
## pikku.config.json
|
|
22
28
|
|
|
23
29
|
```json
|
|
@@ -34,30 +40,74 @@ my-addon/
|
|
|
34
40
|
}
|
|
35
41
|
```
|
|
36
42
|
|
|
43
|
+
`outDir` stays `./.pikku`; `addon: true` is what appends the `addon` segment.
|
|
44
|
+
|
|
37
45
|
## package.json (key fields)
|
|
38
46
|
|
|
39
47
|
```json
|
|
40
48
|
{
|
|
41
49
|
"name": "@my-org/addon-todos",
|
|
42
50
|
"imports": {
|
|
43
|
-
"#pikku/*.js": "
|
|
44
|
-
"#pikku/*": "
|
|
51
|
+
"#pikku/*.js": "./dist/.pikku/*.js",
|
|
52
|
+
"#pikku/*": ["./dist/.pikku/*/index.js", "./dist/.pikku/*"]
|
|
45
53
|
},
|
|
46
54
|
"exports": {
|
|
47
55
|
".": { "types": "./dist/src/index.d.ts", "import": "./dist/src/index.js" },
|
|
48
|
-
"./.pikku/*": "
|
|
49
|
-
"./.pikku/pikku-metadata.gen.json": "
|
|
56
|
+
"./.pikku/*": "./dist/.pikku/addon/*",
|
|
57
|
+
"./.pikku/pikku-metadata.gen.json": "./dist/.pikku/addon/pikku-metadata.gen.json",
|
|
50
58
|
"./.pikku/rpc/pikku-rpc-wirings-map.internal.gen.js": {
|
|
51
|
-
"types": "
|
|
59
|
+
"types": "./dist/.pikku/addon/rpc/pikku-rpc-wirings-map.internal.gen.d.ts"
|
|
52
60
|
}
|
|
53
61
|
},
|
|
54
|
-
"files": ["dist"
|
|
62
|
+
"files": ["dist"],
|
|
55
63
|
"peerDependencies": {
|
|
56
|
-
"@pikku/core": "*"
|
|
64
|
+
"@pikku/core": "*",
|
|
65
|
+
"zod": "^4"
|
|
57
66
|
},
|
|
58
67
|
"scripts": {
|
|
68
|
+
"prebuild": "pikku all",
|
|
59
69
|
"pikku": "pikku all",
|
|
60
|
-
"build": "tsc && cp -r .pikku dist/"
|
|
70
|
+
"build": "tsc && cp -r .pikku types dist/"
|
|
61
71
|
}
|
|
62
72
|
}
|
|
63
73
|
```
|
|
74
|
+
|
|
75
|
+
**`imports` names `dist`, never the source tree.** `files: ["dist"]` is the whole
|
|
76
|
+
published package, and `build` copies `.pikku` and `types` into it — so a
|
|
77
|
+
`#pikku/*` target under `./.pikku/` resolves for the author and for nobody else.
|
|
78
|
+
It is a silent break: the addon compiles, packs, installs and then throws
|
|
79
|
+
`Cannot find module '.../.pikku/addon/function/index.ts'` on first import in the
|
|
80
|
+
consuming app, out of a file the consumer never wrote. The addon's own build
|
|
81
|
+
does not read `imports` at all — tsconfig `paths` covers it, which is why the
|
|
82
|
+
two maps point at different trees.
|
|
83
|
+
|
|
84
|
+
**`exports` targets carry the `addon` segment; the subpaths do not.** A consumer
|
|
85
|
+
writes `@my-org/addon-todos/.pikku/rpc/...`, exactly as it would in an
|
|
86
|
+
application, and the leaf stays the package's own business.
|
|
87
|
+
|
|
88
|
+
## tsconfig.json (key fields)
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"compilerOptions": {
|
|
93
|
+
"module": "NodeNext",
|
|
94
|
+
"moduleResolution": "NodeNext",
|
|
95
|
+
"rootDir": ".",
|
|
96
|
+
"outDir": "./dist",
|
|
97
|
+
"paths": {
|
|
98
|
+
"#pikku/*.js": ["./.pikku/*.ts"],
|
|
99
|
+
"#pikku/*": ["./.pikku/*/index.ts", "./.pikku/*"]
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"include": ["src/**/*", "types/**/*", ".pikku/**/*.ts"],
|
|
103
|
+
"exclude": ["node_modules", "dist", ".pikku/**/*.d.ts"]
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`paths` resolves the source tree because `dist` does not exist yet on the build
|
|
108
|
+
that creates it. Both patterns are needed: the `.js` one reaches a generated
|
|
109
|
+
file (`#pikku/addon/variables/pikku-variables.gen.js`), the bare one reaches a
|
|
110
|
+
leaf's barrel (`#pikku/addon/function`). Keep the `.js` pattern first: both keys
|
|
111
|
+
share the `#pikku/` prefix, and TypeScript takes the first match of the longest
|
|
112
|
+
prefix rather than the most specific pattern. Node sorts by specificity and does
|
|
113
|
+
not care about the order.
|
|
@@ -201,8 +201,7 @@ export const todoAgent = pikkuAgent({
|
|
|
201
201
|
### Scaffold the HTTP surface
|
|
202
202
|
|
|
203
203
|
```bash
|
|
204
|
-
pikku enable agent
|
|
205
|
-
pikku enable agent --noAuth # public
|
|
204
|
+
pikku enable agent
|
|
206
205
|
```
|
|
207
206
|
|
|
208
207
|
The next `pikku all` generates `agent.gen.ts` — run/stream/approve/resume
|
|
@@ -82,10 +82,31 @@ Placeholders are `{{ ... }}`. Resolution order inside a template:
|
|
|
82
82
|
- `{{> footer}}` — include a partial from `partials/`.
|
|
83
83
|
- `{{content}}` / `{{subject}}` — only meaningful inside `partials/layout.html`
|
|
84
84
|
(the rendered body and subject). `layout.html` wraps every template if present.
|
|
85
|
+
- `{{{verifyUrl}}}` — the same value **unescaped**. See below.
|
|
85
86
|
|
|
86
87
|
Locale strings may themselves contain variables and partial-free placeholders, e.g.
|
|
87
|
-
`"subject": "{{inviterName}} invited you to join {{organizationName}}"`.
|
|
88
|
-
|
|
88
|
+
`"subject": "{{inviterName}} invited you to join {{organizationName}}"`. Locale files
|
|
89
|
+
and `theme.json` ship alongside the templates, so they are expanded first and a subject
|
|
90
|
+
of `{{t.invitation.subject}}` expands fully.
|
|
91
|
+
|
|
92
|
+
## Escaping
|
|
93
|
+
|
|
94
|
+
Values are HTML-escaped (`& < > " '`) on the way into `.html` output, so a URL, a
|
|
95
|
+
display name or a font stack containing quotes lands inside its attribute instead of
|
|
96
|
+
breaking out of it. `.subject.txt` and `.text.txt` are plain text and are never escaped.
|
|
97
|
+
|
|
98
|
+
Rendering is **layered by trust**, and the layers do not leak into each other:
|
|
99
|
+
|
|
100
|
+
- Partials are inlined first — a `data` value that happens to contain `{{> footer}}`
|
|
101
|
+
is not an include.
|
|
102
|
+
- `theme.*` and `t.*` are template-author input: expanded next, escaped, and allowed
|
|
103
|
+
to contain further placeholders (up to 5 levels).
|
|
104
|
+
- Everything else is caller data: substituted in **one pass**, escaped, and never
|
|
105
|
+
rescanned — a `data` value containing `{{...}}` renders as those literal characters.
|
|
106
|
+
|
|
107
|
+
`{{content}}` and partials are template-authored markup and stay raw. For a value you
|
|
108
|
+
genuinely want inserted as markup, use the explicit `{{{value}}}` form — it is opt-in,
|
|
109
|
+
it bypasses escaping entirely, and it is only safe for HTML you control.
|
|
89
110
|
|
|
90
111
|
## Typed variables (per template)
|
|
91
112
|
|
|
@@ -66,8 +66,7 @@ see `pikku-services`.
|
|
|
66
66
|
## 2. Enable the server side
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
|
-
yarn pikku enable events
|
|
70
|
-
yarn pikku enable events --noAuth # public events
|
|
69
|
+
yarn pikku enable events
|
|
71
70
|
```
|
|
72
71
|
|
|
73
72
|
This sets `scaffold.events` in `pikku.config.json`. The next `pikku all` generates
|
|
@@ -75,10 +75,12 @@ The `POST /rpc/:rpcName` endpoint that dispatches every `expose: true` function
|
|
|
75
75
|
is **generated, not hand-written**. Turn it on and let codegen own it:
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
|
-
pikku enable rpc # sets scaffold.rpc = true
|
|
79
|
-
pikku enable rpc --noAuth # sets scaffold.rpc = { auth: false } (public)
|
|
78
|
+
pikku enable rpc # sets scaffold.rpc = true
|
|
80
79
|
```
|
|
81
80
|
|
|
81
|
+
The flag says the endpoint exists, not who may call it — each exposed function
|
|
82
|
+
is gated by its own `auth`, permissions and scopes.
|
|
83
|
+
|
|
82
84
|
This writes `rpc-public.gen.ts` with an `rpcCaller` function and its `wireHTTP`
|
|
83
85
|
call already wired. Do not write that wiring yourself — a hand-rolled copy
|
|
84
86
|
collides with the generated route on the same path.
|
|
@@ -682,14 +682,13 @@ Coverage is attributed by running scenarios against a server that is collecting
|
|
|
682
682
|
Prerequisite in `pikku.config.json`:
|
|
683
683
|
|
|
684
684
|
```bash
|
|
685
|
-
pikku enable scenarios # sets scaffold.scenarios = true
|
|
686
|
-
pikku enable scenarios --noAuth # sets scaffold.scenarios = { "auth": false }
|
|
685
|
+
pikku enable scenarios # sets scaffold.scenarios = true
|
|
687
686
|
```
|
|
688
687
|
|
|
689
|
-
`scaffold.scenarios` is a boolean or `{
|
|
690
|
-
|
|
691
|
-
under a shape where a string could be a path, silently reading
|
|
692
|
-
would be worse than failing.
|
|
688
|
+
`scaffold.scenarios` is a boolean or `{ path? }` — whether the surface exists
|
|
689
|
+
and where it is written. A bare string is **rejected by the config loader**, not
|
|
690
|
+
reinterpreted: under a shape where a string could be a path, silently reading
|
|
691
|
+
one as a flag would be worse than failing.
|
|
693
692
|
|
|
694
693
|
`scaffold.scenarios` generates the coverage and stub RPCs into your project (`pikkuScenarioTakeLiveCoverage`, `pikkuScenarioResetLiveCoverage`, `pikkuScenarioResetStubs`, `pikkuScenarioGetStubCalls`), so scenario runs work against any server. The coverage RPC reads `<outDir>/function/pikku-functions-meta-verbose.gen.json` off disk at request time — codegen always writes it, but it has to be deployed alongside the app or the RPC returns `null`.
|
|
695
694
|
|