@pikku/skills 0.12.28 → 0.12.29
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 +10 -0
- package/dist/skills.gen.js +1 -1
- package/package.json +1 -1
- package/skills/pikku-addon/SKILL.md +53 -2
- package/skills/pikku-addon/references/addon-package-manifest.md +2 -1
- package/skills/pikku-agent/SKILL.md +4 -2
- package/skills/pikku-agent/references/agents.md +30 -0
- package/skills/pikku-build/SKILL.md +5 -3
- package/skills/pikku-build/references/app.md +45 -9
- package/skills/pikku-build/references/design.md +128 -0
- package/skills/pikku-fabric/SKILL.md +33 -0
package/package.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
name: pikku-addon
|
|
3
3
|
description: >-
|
|
4
4
|
Use when creating or consuming reusable function packages (addons) in Pikku. Covers wireAddon,
|
|
5
|
-
ref(), pikkuAddonServices, pikkuAddonWireServices, addon package structure,
|
|
6
|
-
function sharing. TRIGGER when: code uses wireAddon/ref()/pikkuAddonServices, user asks about
|
|
5
|
+
ref(), pikkuAddonServices, pikkuAddonWireServices, addon package structure, addons that ship
|
|
6
|
+
database tables (pikku db export), and cross-project function sharing. TRIGGER when: code uses wireAddon/ref()/pikkuAddonServices, user asks about
|
|
7
7
|
addons, reusable function packages, cross-project sharing, or addon package structure. DO NOT
|
|
8
8
|
TRIGGER when: user asks about internal function composition (use pikku-wiring) or general function
|
|
9
9
|
definitions (use pikku-concepts).
|
|
@@ -264,6 +264,49 @@ addon` above is the exception — it runs before the addon, and its CLI, exist.
|
|
|
264
264
|
addon installs fine and fails to typecheck in every app that depends on it —
|
|
265
265
|
which is what `pikku validate` is there to catch before you publish.
|
|
266
266
|
|
|
267
|
+
### Database tables
|
|
268
|
+
|
|
269
|
+
An addon may **ship** tables. It must never **create** them: it runs inside the
|
|
270
|
+
consumer, against the consumer's database, so a boot-time `CREATE TABLE` puts a
|
|
271
|
+
second authority on a schema the consumer's migrations own. It declares instead,
|
|
272
|
+
and the consumer's migration history absorbs the declaration.
|
|
273
|
+
|
|
274
|
+
Author the DDL per dialect:
|
|
275
|
+
|
|
276
|
+
```text
|
|
277
|
+
db/sqlite/0001-labels.sql
|
|
278
|
+
db/postgres/0001-labels.sql
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
`pikku all` publishes `<outDir>/db/pikku-db-meta.gen.json` on every build — per
|
|
282
|
+
dialect, the SQL verbatim plus a table/column map — and writes it **empty** when
|
|
283
|
+
the addon has no tables, because a consumer reads an absent file as a package
|
|
284
|
+
that cannot say. (`pikku db export` writes the same file on demand.) The
|
|
285
|
+
consumer resolves it **through the package name**, so it must be exported and
|
|
286
|
+
packed, or it never arrives:
|
|
287
|
+
|
|
288
|
+
```json
|
|
289
|
+
{
|
|
290
|
+
"exports": {
|
|
291
|
+
"./.pikku/db/pikku-db-meta.gen.json": "./dist/.pikku/addon/db/pikku-db-meta.gen.json"
|
|
292
|
+
},
|
|
293
|
+
"files": ["dist"]
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**An unresolvable artifact stops `db generate`.** Because the file is
|
|
298
|
+
unconditional, absence means the package cannot say whether it ships tables —
|
|
299
|
+
either it was built with an older CLI, or `exports`/`files` do not carry it. The
|
|
300
|
+
error names both causes. An addon with genuinely no tables is *not* this case:
|
|
301
|
+
it publishes `{}` and is waved through.
|
|
302
|
+
|
|
303
|
+
Two more loud ones: a malformed artifact (missing the SQL for a dialect it
|
|
304
|
+
claims) is rejected rather than half-applied, and an addon publishing only a
|
|
305
|
+
dialect the consumer does not use gets an error naming what it does support.
|
|
306
|
+
|
|
307
|
+
`verifiers/db-schema` runs this end to end on both dialects — copy its addon
|
|
308
|
+
`package.json` when wiring a new one.
|
|
309
|
+
|
|
267
310
|
## Consuming an Addon
|
|
268
311
|
|
|
269
312
|
### Install & Register
|
|
@@ -281,6 +324,14 @@ wireAddon({ name: 'todos', package: '@my-org/addon-todos' })
|
|
|
281
324
|
|
|
282
325
|
After registration, run `yarn pikku all` to generate types for the addon's functions.
|
|
283
326
|
|
|
327
|
+
If the addon ships tables, `pikku db generate` then writes one migration per
|
|
328
|
+
addon — named after the package, carrying the addon's own SQL — after Better
|
|
329
|
+
Auth's and the runtime's, so an addon table may reference `user` or a runtime
|
|
330
|
+
table. `pikku db migrate` applies it; re-running `generate` writes nothing once
|
|
331
|
+
covered, and writes only the delta after an addon upgrade. An addon wired with
|
|
332
|
+
`wireRemoteAddon` contributes no schema at all: its tables belong to the
|
|
333
|
+
deployment that runs its functions.
|
|
334
|
+
|
|
284
335
|
### Call via RPC
|
|
285
336
|
|
|
286
337
|
```typescript
|
|
@@ -57,7 +57,8 @@ package that declared them, and the extra segment is what stops a linked addon's
|
|
|
57
57
|
"./.pikku/pikku-metadata.gen.json": "./dist/.pikku/addon/pikku-metadata.gen.json",
|
|
58
58
|
"./.pikku/rpc/pikku-rpc-wirings-map.internal.gen.js": {
|
|
59
59
|
"types": "./dist/.pikku/addon/rpc/pikku-rpc-wirings-map.internal.gen.d.ts"
|
|
60
|
-
}
|
|
60
|
+
},
|
|
61
|
+
"./.pikku/db/pikku-db-meta.gen.json": "./dist/.pikku/addon/db/pikku-db-meta.gen.json"
|
|
61
62
|
},
|
|
62
63
|
"files": ["dist"],
|
|
63
64
|
"peerDependencies": {
|
|
@@ -23,7 +23,7 @@ than what they look like.
|
|
|
23
23
|
|
|
24
24
|
| You are… | Read |
|
|
25
25
|
| --- | --- |
|
|
26
|
-
| Defining or invoking an agent — tools, memory, streaming, approval, threads | `references/agents.md` |
|
|
26
|
+
| Defining or invoking an agent — tools, memory, streaming, approval, threads, images | `references/agents.md` |
|
|
27
27
|
| Wiring the runner, or pointing model strings at a provider or gateway | `references/runner-vercel.md` |
|
|
28
28
|
| Adding speech in or out of an agent | `references/voice.md` |
|
|
29
29
|
|
|
@@ -44,7 +44,9 @@ session, the credentials and the RPC depth for you.
|
|
|
44
44
|
key. `role`, `personality` and `goal` are concatenated in that order and
|
|
45
45
|
nothing validates which text lands where, so the split buys legibility only.
|
|
46
46
|
- **`output` is honoured only when the agent has no tools.** A structured-output
|
|
47
|
-
schema on a tool-calling agent is silently inert.
|
|
47
|
+
schema on a tool-calling agent is silently inert. The pairing that wants it is
|
|
48
|
+
reading an image or a document into typed data — one tool-free agent, an
|
|
49
|
+
`output` schema, and the picture passed as an `attachments` entry.
|
|
48
50
|
- **`auth` defaults to `false`**, because agents are normally invoked from an
|
|
49
51
|
already-authenticated `pikkuFunc`. `scopes` and `permissions` are enforced
|
|
50
52
|
either way — see `pikku-auth`.
|
|
@@ -201,6 +201,36 @@ export const structuredAgent = pikkuAgent({
|
|
|
201
201
|
})
|
|
202
202
|
```
|
|
203
203
|
|
|
204
|
+
### Images and files
|
|
205
|
+
|
|
206
|
+
`attachments` on the agent input is how a picture, a scan or a PDF reaches the
|
|
207
|
+
model. Each entry carries **either** `data` (base64, no `data:` prefix) **or**
|
|
208
|
+
`url`, plus a `mediaType`:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
const { object } = await rpc.agent.run('read-receipt', {
|
|
212
|
+
message: 'List every line item on this receipt.',
|
|
213
|
+
threadId, resourceId,
|
|
214
|
+
attachments: [{ type: 'image', data: base64Jpeg, mediaType: 'image/jpeg' }],
|
|
215
|
+
})
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
- **`url` is downloaded server-side**, by the runner, before the model sees it —
|
|
219
|
+
so a caller-supplied URL is an SSRF surface. `VercelAgentRunner`'s third
|
|
220
|
+
constructor argument is a host allowlist; set it whenever an HTTP wiring
|
|
221
|
+
accepts attachment URLs, or take `data` and never accept a URL at all.
|
|
222
|
+
- **The model has to be a vision model.** The provider prefix does not decide
|
|
223
|
+
this — `openai/gpt-5-mini` reads images, a text-only id in the same family
|
|
224
|
+
answers as though the attachment were not there rather than erroring.
|
|
225
|
+
- **Reading an image into data is the tool-free case**, so it can use `output`:
|
|
226
|
+
an agent with an `output` schema and no tools resolves `result.object` as the
|
|
227
|
+
typed extraction. Add one tool and you get prose back instead — see
|
|
228
|
+
**Structured output** above.
|
|
229
|
+
- **Base64 is the request body.** A phone photo is measured in megabytes and
|
|
230
|
+
goes through your function's input schema, the RPC payload and the model's
|
|
231
|
+
context. Downscale on the client before uploading — the model does not read
|
|
232
|
+
the pixels you paid to send.
|
|
233
|
+
|
|
204
234
|
### Narrowing tools per step
|
|
205
235
|
|
|
206
236
|
`prepareStep` runs before each step with the live tool array for that step, so
|
|
@@ -33,9 +33,11 @@ plus more effort" — it is App plus a deliberate surface checklist, so read the
|
|
|
33
33
|
base first and follow it in full rather than blending the two into one plan.
|
|
34
34
|
|
|
35
35
|
The supporting references belong to whichever mode sends you to them:
|
|
36
|
-
`references/multi-app.md` (a second frontend), `references/
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
`references/multi-app.md` (a second frontend), `references/design.md` (committing
|
|
37
|
+
to a design direction and judging whether the screens realise it — read before
|
|
38
|
+
the first screen is built, not after the last), `references/theming.md`
|
|
39
|
+
(authoring the theme),
|
|
40
|
+
`references/ship.md` (deploying, and the Fabric-readiness contract).
|
|
39
41
|
|
|
40
42
|
## Bootstrap before anything else
|
|
41
43
|
|
|
@@ -418,6 +418,12 @@ over, and an uncovered function is a half-milestone whether or not the note says
|
|
|
418
418
|
file. Compose the kit from `@/components/<Name>` rather than hand-rolling.
|
|
419
419
|
Register the screen in `useNavItems()` — that one file feeds both the desktop
|
|
420
420
|
sidebar and the phone navigation.
|
|
421
|
+
**Read `references/design.md` before you write the first screen.** You commit
|
|
422
|
+
to a design direction there and are then accountable to it — it hands you no
|
|
423
|
+
layouts, because the design is yours to make. Screenshot each screen at 390
|
|
424
|
+
and 1440 with the seed in place at the END of every milestone, and look at the
|
|
425
|
+
images — not once at §8, where the only affordable fix is a repaint of eight
|
|
426
|
+
screens.
|
|
421
427
|
6. **Scenario** (§7), then `status: built`.
|
|
422
428
|
|
|
423
429
|
Rules that are not optional:
|
|
@@ -635,17 +641,29 @@ loud is a number nobody acts on.
|
|
|
635
641
|
|
|
636
642
|
## 8. Make it look like someone designed it
|
|
637
643
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
+
**This section numbers 8, but half of it has already happened.** Read
|
|
645
|
+
`references/design.md` before the first screen is built — a design pass run on
|
|
646
|
+
eight milestones of scaffolded screens is a repaint, and it shows. What is left
|
|
647
|
+
here at §8 is the theme you may have deferred and the critique you cannot run
|
|
648
|
+
until there are screens to critique.
|
|
649
|
+
|
|
650
|
+
Three separate jobs, and conflating them is why open-source builds come out
|
|
651
|
+
looking like the template:
|
|
652
|
+
|
|
653
|
+
- **Direction** — deciding what it should look like. **No open-source tool does
|
|
654
|
+
this.** Fabric has `fabric-theme`; you have §1's answer and 8a below.
|
|
655
|
+
- **Execution** — whether the screens actually realise that direction, or
|
|
656
|
+
default to whatever component was nearest. No theme does this, and it is where
|
|
657
|
+
"works but looks like nobody decided anything" comes from.
|
|
658
|
+
`references/design.md` carries the process for it, and it belongs at §6, per
|
|
659
|
+
screen.
|
|
660
|
+
- **Critique** — judging how well the built screens execute the direction.
|
|
644
661
|
`impeccable` does this well, and it is free.
|
|
645
662
|
|
|
646
663
|
Impeccable audits the design you chose. It will never tell you the app should
|
|
647
664
|
have looked like something else — it will happily award a clean bill of health to
|
|
648
|
-
a perfectly-executed default. Skip
|
|
665
|
+
a perfectly-executed default. Skip the first two and you ship Neutral with good
|
|
666
|
+
spacing.
|
|
649
667
|
|
|
650
668
|
### 8a. Author the theme — the step nothing does for you
|
|
651
669
|
|
|
@@ -674,6 +692,13 @@ theme is; only the note records why.
|
|
|
674
692
|
|
|
675
693
|
### 8b. Compose real components, then critique
|
|
676
694
|
|
|
695
|
+
**`references/design.md` is where design actually lives** — committing to a
|
|
696
|
+
direction before the first screen, judging the result from screenshots rather
|
|
697
|
+
than from source, the two screens that get skipped, and why you reset the dev
|
|
698
|
+
database before judging anything. It prescribes no layouts on purpose: two apps
|
|
699
|
+
built from this skill should not look like each other. What follows here is only
|
|
700
|
+
the component inventory.
|
|
701
|
+
|
|
677
702
|
**Compose with Mantine's rich components — not tables and text everywhere:**
|
|
678
703
|
|
|
679
704
|
- **`@mantine/charts`** (Recharts underneath) for overviews — `AreaChart`,
|
|
@@ -710,8 +735,17 @@ a modal taller than the viewport. Mantine gives you the tools (responsive `Grid`
|
|
|
710
735
|
them. The template already mounts a phone navigation per `AGENTS.md` — pick
|
|
711
736
|
`MobileTabBar` or `MobileNavDrawer` deliberately per app, never both.
|
|
712
737
|
|
|
713
|
-
The gate: **no P0 findings left on any screen, in any app, at either width
|
|
714
|
-
|
|
738
|
+
The gate: **no P0 findings left on any screen, in any app, at either width**,
|
|
739
|
+
and every screen honestly answers the questions in `references/design.md` —
|
|
740
|
+
including whether it looks like the direction you committed to or like the
|
|
741
|
+
components you had. Don't silence a finding by deleting the feature it is
|
|
742
|
+
about.
|
|
743
|
+
|
|
744
|
+
**Critique the data too, not just the layout.** Scenario runs write run-tagged
|
|
745
|
+
rows into the dev database, so by §8 the app is full of `Ripe peaches d193e2aa`
|
|
746
|
+
seven copies deep. Run `pikku db reset` and restart the dev server before you
|
|
747
|
+
screenshot anything — a screen judged against that data gets designed for a
|
|
748
|
+
problem it does not have.
|
|
715
749
|
|
|
716
750
|
## 9. Ship it, and stay Fabric-ready
|
|
717
751
|
|
|
@@ -735,6 +769,8 @@ cheaper to honour than to retrofit:
|
|
|
735
769
|
|
|
736
770
|
- `references/multi-app.md` — adding a second frontend (§4), at the milestone
|
|
737
771
|
that needs it
|
|
772
|
+
- `references/design.md` — committing to a design direction, and how to tell
|
|
773
|
+
whether the screens realise it. Read BEFORE the first screen (§6), not at §8
|
|
738
774
|
- `references/theming.md` — authoring the theme (§8a)
|
|
739
775
|
- `references/ship.md` — deploying, and the Fabric-readiness contract (§9)
|
|
740
776
|
- Sibling skills: `pikku-knowledge` (§2), `pikku-auth` (§3),
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Design
|
|
2
|
+
|
|
3
|
+
Your job here is to design something worth the product — not to apply a house
|
|
4
|
+
style. **There is no house style, and this file is not one.** Two apps built from
|
|
5
|
+
this skill should not look like each other; if they do, something has gone wrong
|
|
6
|
+
that no amount of spacing will fix.
|
|
7
|
+
|
|
8
|
+
So: no prescribed layouts, no component rules, no ratios. What follows is the
|
|
9
|
+
process that makes freedom accountable, the handful of facts that are not
|
|
10
|
+
matters of taste, and the symptoms of a screen nobody actually designed.
|
|
11
|
+
|
|
12
|
+
## Commit to a direction before the first screen
|
|
13
|
+
|
|
14
|
+
An agent given "make it look good" and nothing else defaults — to the component
|
|
15
|
+
nearest to hand, on every screen, in every app. Not because it lacks taste, but
|
|
16
|
+
because there is nothing to be wrong against. A direction fixes that: state one,
|
|
17
|
+
in words, before any screen exists.
|
|
18
|
+
|
|
19
|
+
Say what this product *is* — its register, its reference points, what it feels
|
|
20
|
+
like to use, what it is deliberately not. "A warm, food-forward thing you use
|
|
21
|
+
standing at an open fridge door, closer to a recipe card than to a dashboard, and
|
|
22
|
+
never clinical" is a direction. "Clean and modern" is not — it rules nothing out,
|
|
23
|
+
so it cannot be departed from.
|
|
24
|
+
|
|
25
|
+
Write it to `knowledge/decisions/design/`, then build the theme from it
|
|
26
|
+
(`references/theming.md`). **From that point you are accountable to your own
|
|
27
|
+
direction, not to this file.** That is the whole mechanism: the freedom is real,
|
|
28
|
+
and so is the commitment.
|
|
29
|
+
|
|
30
|
+
Be ambitious with it. A direction that could describe any SaaS app has not been
|
|
31
|
+
chosen — it has been defaulted to in words instead of in components.
|
|
32
|
+
|
|
33
|
+
## One screen, designed properly, before the rest exist
|
|
34
|
+
|
|
35
|
+
Design the first real screen as if it were the only one, and take it further than
|
|
36
|
+
feels necessary. Every screen after it inherits its register — its density, its
|
|
37
|
+
rhythm, what a row of data looks like, how state is signalled. That inheritance
|
|
38
|
+
happens whether you plan it or not, so make the first one worth inheriting.
|
|
39
|
+
|
|
40
|
+
This is also the cheapest design work in the project. The eighth screen is a
|
|
41
|
+
repaint of eight; the first is a decision.
|
|
42
|
+
|
|
43
|
+
## Judge it — and don't grade your own homework
|
|
44
|
+
|
|
45
|
+
Models rate their own output generously, and "does this look good?" answered by
|
|
46
|
+
the thing that made it is always yes. Use evidence.
|
|
47
|
+
|
|
48
|
+
- **Screenshot every screen and look at the image**, at ~390px and at ~1440px.
|
|
49
|
+
Judging your own UI from source is guessing, and the failures that matter —
|
|
50
|
+
proportion, hierarchy, a wall of identical boxes — are invisible in JSX.
|
|
51
|
+
- **Run `impeccable`** (`npx impeccable install`, Node 22.18+) and feed it the
|
|
52
|
+
screenshots. It is external, it does not flatter, and it scores execution
|
|
53
|
+
against interaction heuristics. But it audits how well you executed the design
|
|
54
|
+
you chose — it will award a clean bill of health to a perfectly executed
|
|
55
|
+
default. It checks step 2; it never replaces it.
|
|
56
|
+
- **Look at every milestone, not once at the end.** A screen that was fine at
|
|
57
|
+
three rows is a different screen at sixty, and the milestone that added the
|
|
58
|
+
sixty is the cheapest place to notice.
|
|
59
|
+
|
|
60
|
+
Questions worth answering honestly, per screen. The answers are yours; only the
|
|
61
|
+
questions are fixed:
|
|
62
|
+
|
|
63
|
+
1. What question does someone open this screen to ask, and how fast do they get
|
|
64
|
+
the answer?
|
|
65
|
+
2. What can be understood before reading a word?
|
|
66
|
+
3. What is here that is not earning its space?
|
|
67
|
+
4. What does it look like at real volume, and at 390px?
|
|
68
|
+
5. Does it look like the direction you committed to — or like the components you
|
|
69
|
+
had?
|
|
70
|
+
6. Would you show it to the user without apologising for it?
|
|
71
|
+
|
|
72
|
+
## Facts, not taste
|
|
73
|
+
|
|
74
|
+
These are not design opinions and are not open to a different answer.
|
|
75
|
+
|
|
76
|
+
- **Reset the dev database before judging anything.** Scenario runs deliberately
|
|
77
|
+
tag rows with a run id so assertions do not collide, so after a few runs the
|
|
78
|
+
app is full of `Ripe peaches d193e2aa`, seven copies deep. Nobody's data looks
|
|
79
|
+
like that, and a screen designed against it is designed for a problem it does
|
|
80
|
+
not have. `pikku db reset` replays the migrations and the seed — and the dev
|
|
81
|
+
server holds an open handle to the file, so **restart it after**, or every call
|
|
82
|
+
fails with `disk I/O error` and the app looks broken for reasons that are not
|
|
83
|
+
the app.
|
|
84
|
+
- **Seed generously and realistically.** A seed with a deliberate spread designs
|
|
85
|
+
the hard cases for you. Three identical rows teach you nothing.
|
|
86
|
+
- **Never render a sign-in method that is not configured.** A "Continue with
|
|
87
|
+
Google" button on an app with no Google credentials is a dead control on the
|
|
88
|
+
first screen anyone sees. Render social buttons from what `socialProviders`
|
|
89
|
+
actually declares — and when it declares none, the divider goes too.
|
|
90
|
+
- **Empty, loading and error are states that exist.** The empty state is what a
|
|
91
|
+
new user meets first and the one most often skipped entirely.
|
|
92
|
+
- **Contrast and tap targets are measured, not judged.** `pikku-a11y` covers it.
|
|
93
|
+
One trap worth knowing: a Mantine `light` variant paints its label at the
|
|
94
|
+
generated ramp's stop, which lands just under AA on its own tint — name the
|
|
95
|
+
darker ink once and reuse it.
|
|
96
|
+
|
|
97
|
+
## Two screens that get skipped
|
|
98
|
+
|
|
99
|
+
Not rules about how they should look — only that they are yours to design.
|
|
100
|
+
|
|
101
|
+
**The login page is the entry point.** It is the first thing anyone sees, where
|
|
102
|
+
every demo starts, and routinely the least designed screen in the app: a default
|
|
103
|
+
card with a wordmark, saying "scaffold" before the product has said anything. It
|
|
104
|
+
gets the same direction as everything else.
|
|
105
|
+
|
|
106
|
+
**The navigation is on every screen**, which makes it the highest-leverage
|
|
107
|
+
surface you have. The scaffold mounts one that works; working is not the same as
|
|
108
|
+
considered. Decide the destinations and their number deliberately, and style it
|
|
109
|
+
through the theme rather than leaving the component defaults — the same look on
|
|
110
|
+
every app is the tell.
|
|
111
|
+
|
|
112
|
+
## Symptoms of a screen nobody designed
|
|
113
|
+
|
|
114
|
+
If a screen shows these, you defaulted. **The fix is yours to choose** — these
|
|
115
|
+
are a diagnosis, not a prescription, and the interesting answer is rarely the
|
|
116
|
+
first one.
|
|
117
|
+
|
|
118
|
+
- The top of the screen is a form, and the content it acts on starts below the
|
|
119
|
+
fold.
|
|
120
|
+
- Two adjacent surfaces do the same job because both were added separately.
|
|
121
|
+
- Everything is the same size, weight and colour, so nothing can be found
|
|
122
|
+
without reading it.
|
|
123
|
+
- The same fact is stated twice in one line, in two different components.
|
|
124
|
+
- A section named for an exception holds half the data.
|
|
125
|
+
- Every row carries the same buttons, and the buttons outweigh the content.
|
|
126
|
+
- The palette's meaningful colours are also used decoratively, so they have
|
|
127
|
+
stopped meaning anything.
|
|
128
|
+
- It looks like the last app you built.
|
|
@@ -285,6 +285,39 @@ script, run `pikku dev` from the **project root** (it resolves `srcDirectories`
|
|
|
285
285
|
relative to the config, so a nested cwd yields a doubled watch path and no hot
|
|
286
286
|
reload).
|
|
287
287
|
|
|
288
|
+
## Reaching a model
|
|
289
|
+
|
|
290
|
+
An agent needs an `agentRunner` in singleton services, and locally you do not
|
|
291
|
+
write one: `pikku dev` builds it from env when it finds a **matching pair** —
|
|
292
|
+
`OPENAI_BASE_URL` + `OPENAI_API_KEY`, or `LITELLM_PROXY_URL` + `LITELLM_API_KEY`
|
|
293
|
+
— and registers it under `'*'`, so every `provider/model` prefix resolves
|
|
294
|
+
through it. With neither pair complete it builds nothing and every agent call
|
|
295
|
+
fails with `AIProviderNotConfiguredError` (a 503) — which reads like a broken
|
|
296
|
+
agent rather than a missing key, so check `.env` first. Mixing halves is worse
|
|
297
|
+
than missing them: a URL from one source with a key from the other 401s on every
|
|
298
|
+
call, so the pairs are taken whole, OpenAI first.
|
|
299
|
+
|
|
300
|
+
Two ways to fill them in:
|
|
301
|
+
|
|
302
|
+
**The Fabric AI gateway.** One key, and every model the gateway fronts —
|
|
303
|
+
OpenAI, Anthropic, Google, the OpenRouter catalogue — is reachable by id, billed
|
|
304
|
+
through your Fabric account rather than per-vendor:
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
pikku fabric login
|
|
308
|
+
pikku fabric llm key --env >> .env # writes both pairs; --shell and --json also exist
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
It mints or reuses a developer-scoped key against your Fabric login. `link` is
|
|
312
|
+
not required — the key is yours, not the project's.
|
|
313
|
+
|
|
314
|
+
**Your own vendor key.** `OPENAI_BASE_URL=https://api.openai.com/v1` with your
|
|
315
|
+
`OPENAI_API_KEY`, and model ids are then only the ones that vendor serves.
|
|
316
|
+
|
|
317
|
+
A deployed stage takes the same names through `pikku fabric secrets set` /
|
|
318
|
+
`variables set` — the agent units get their runner wired by the bundler, from
|
|
319
|
+
those values.
|
|
320
|
+
|
|
288
321
|
## Deploy
|
|
289
322
|
|
|
290
323
|
```bash
|