@getrheo/rheo-skill 1.0.0
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/README.md +76 -0
- package/package.json +51 -0
- package/rheo/SKILL.md +32 -0
- package/rheo/rheo-best-practices/SKILL.md +46 -0
- package/rheo/rheo-best-practices/examples/react-native-install-snippet.md +23 -0
- package/rheo/rheo-best-practices/examples/swiftui-install-snippet.md +20 -0
- package/rheo/rheo-best-practices/references/implement-workflow.md +27 -0
- package/rheo/rheo-best-practices/references/integrations.md +51 -0
- package/rheo/rheo-best-practices/references/react-native-bare.md +36 -0
- package/rheo/rheo-best-practices/references/react-native-expo.md +49 -0
- package/rheo/rheo-best-practices/references/swiftui.md +52 -0
- package/rheo/rheo-best-practices/references/troubleshooting.md +57 -0
- package/rheo/rheo-flow-import/SKILL.md +68 -0
- package/rheo/rheo-flow-import/examples/branching-onboarding.manifest.json +155 -0
- package/rheo/rheo-flow-import/examples/flow-spec.example.json +56 -0
- package/rheo/rheo-flow-import/examples/linear-onboarding.manifest.json +104 -0
- package/rheo/rheo-flow-import/examples/revenuecat-paywall.manifest.json +154 -0
- package/rheo/rheo-flow-import/references/animation-import.md +79 -0
- package/rheo/rheo-flow-import/references/capabilities.md +107 -0
- package/rheo/rheo-flow-import/references/carousel-import.md +64 -0
- package/rheo/rheo-flow-import/references/flow-spec.md +214 -0
- package/rheo/rheo-flow-import/references/font-import.md +67 -0
- package/rheo/rheo-flow-import/references/import-workflow.md +240 -0
- package/rheo/rheo-flow-import/references/layer-schema-pitfalls.md +179 -0
- package/rheo/rheo-flow-import/references/localization-import.md +128 -0
- package/rheo/rheo-flow-import/references/manifest-agent-profile-fallback.md +74 -0
- package/rheo/rheo-flow-import/references/manifest-rules.md +197 -0
- package/rheo/rheo-flow-import/references/publish-gates.md +91 -0
- package/rheo/rheo-flow-import/references/react-native-source-patterns.md +99 -0
- package/rheo/rheo-flow-import/references/swiftui-source-patterns.md +99 -0
- package/rheo/rheo-flow-import/scripts/audit-import.mjs +4 -0
- package/rheo/rheo-flow-import/scripts/audit-publish-manifest.mjs +4 -0
- package/rheo/rheo-flow-import/scripts/fetch-profile.mjs +4 -0
- package/rheo/rheo-flow-import/scripts/lib/rheo-cli.mjs +12753 -0
- package/rheo/rheo-flow-import/scripts/normalize-manifest.mjs +4 -0
- package/rheo/rheo-flow-import/scripts/print-manifest-summary.mjs +4 -0
- package/rheo/rheo-flow-import/scripts/scaffold-manifest.mjs +4 -0
- package/rheo/rheo-flow-import/scripts/validate-manifest.mjs +4 -0
- package/src/index.ts +32 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Carousel import rules
|
|
2
|
+
|
|
3
|
+
Use when the source flow has an **in-screen pager** (`infoSteps`, `currentInfoStep`, horizontal `FlatList` / `PagerView`, dot indicators).
|
|
4
|
+
|
|
5
|
+
## Rheo carousel behavior (swipe-only)
|
|
6
|
+
|
|
7
|
+
- Users move between slides by **swiping** (horizontal scroll with snap).
|
|
8
|
+
- Optional **`pageControl`** adds dot indicators — not buttons.
|
|
9
|
+
- There is **no** Next / Continue button on the carousel layer in the manifest or at runtime.
|
|
10
|
+
- On the **last slide** (when `loop` is false and there are 2+ slides), swiping to that slide emits a carousel completion so `screen.next` can run.
|
|
11
|
+
- **Single-slide** carousels do not auto-complete; add a `regions.footer` **Continue** (or other) button to advance the flow.
|
|
12
|
+
- **`loop: true`** carousels never auto-complete; pair with a separate screen-level CTA when the flow should move on.
|
|
13
|
+
|
|
14
|
+
## Manifest shape
|
|
15
|
+
|
|
16
|
+
- One `kind: "carousel"` in `regions.body` (or inside a body stack).
|
|
17
|
+
- Each page → child **`stack`** in `carousel.slides[]`.
|
|
18
|
+
- `pageControl: { "position": "bottom" }` when source had dot indicators.
|
|
19
|
+
|
|
20
|
+
## Do not duplicate paging UI
|
|
21
|
+
|
|
22
|
+
| Source | Import |
|
|
23
|
+
|--------|--------|
|
|
24
|
+
| Pager pages | `carousel.slides[]` |
|
|
25
|
+
| Dot indicators | `pageControl` only |
|
|
26
|
+
| Swipe between pages | Default — no extra buttons |
|
|
27
|
+
| Footer that only increments pager index | **Omit** — not `regions.footer` |
|
|
28
|
+
| Footer / CTA that exits to **next route** | `regions.footer` with `continue` / `go_to_step`, or rely on last-slide swipe completion + `screen.next` |
|
|
29
|
+
|
|
30
|
+
## Example (swipe + dots)
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"regions": {
|
|
35
|
+
"body": {
|
|
36
|
+
"kind": "stack",
|
|
37
|
+
"direction": "vertical",
|
|
38
|
+
"children": [
|
|
39
|
+
{
|
|
40
|
+
"kind": "carousel",
|
|
41
|
+
"slides": [
|
|
42
|
+
{
|
|
43
|
+
"kind": "stack",
|
|
44
|
+
"direction": "vertical",
|
|
45
|
+
"align": "center",
|
|
46
|
+
"children": [
|
|
47
|
+
{ "kind": "text", "text": { "default": "Slide 1" }, "style": { "color": "#111111" } }
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"pageControl": { "position": "bottom" }
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"next": { "default": "scr_next" }
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Completion gate
|
|
61
|
+
|
|
62
|
+
- [ ] No `button` layers for in-pager next/continue.
|
|
63
|
+
- [ ] `screen.next` targets the next **screen** in the flow, not “next slide”.
|
|
64
|
+
- [ ] Single-slide carousel screens include a flow-level CTA if the flow must advance without swiping.
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Flow Spec (recommended authoring path)
|
|
2
|
+
|
|
3
|
+
A **flow spec** is a compact, agent-authored description of a flow. Running
|
|
4
|
+
`node scripts/scaffold-manifest.mjs <flow-spec.json>` expands it into a
|
|
5
|
+
**schema-valid** Rheo `FlowManifest` skeleton: it generates `scr_*`/`lyr_*` ids,
|
|
6
|
+
wraps label copy into `children`, builds `optionBindings` and `branching` for
|
|
7
|
+
choices, maps button variants to the Rheo enum, and wires `next` edges. You then
|
|
8
|
+
enrich theme/styles and validate.
|
|
9
|
+
|
|
10
|
+
> The scaffold guarantees **schema validity only**. It does not invent colors,
|
|
11
|
+
> fonts, or publish-gate styling — set `theme` and layer `style` from audit
|
|
12
|
+
> evidence, then run `validate-manifest.mjs` + `audit-publish-manifest.mjs`.
|
|
13
|
+
|
|
14
|
+
Why use it: the scaffold removes the mistakes that cause `invalid_union` import
|
|
15
|
+
failures (missing `children`, UUID-as-layer-id, missing choice bindings, raw
|
|
16
|
+
source `variant` names). Hand-authoring is allowed but you own every mechanical
|
|
17
|
+
detail yourself.
|
|
18
|
+
|
|
19
|
+
## Top-level shape
|
|
20
|
+
|
|
21
|
+
```jsonc
|
|
22
|
+
{
|
|
23
|
+
"flowId": "00000000-0000-0000-0000-000000000001", // optional placeholder; dashboard replaces it
|
|
24
|
+
"version": 1,
|
|
25
|
+
"defaultLocale": "en",
|
|
26
|
+
"locales": ["en"],
|
|
27
|
+
"entry": "scr_welcome", // optional; defaults to the first screen's id
|
|
28
|
+
"theme": { /* @getrheo/contracts Theme */ },
|
|
29
|
+
"sdkAttributeKeys": ["sdk.plan"], // non-reserved keys used by decisions
|
|
30
|
+
"screens": [ /* ScreenSpec[] — required */ ],
|
|
31
|
+
"decisions": [ /* @getrheo/contracts DecisionNode[] */ ],
|
|
32
|
+
"externalSurfaces": [ /* ExternalSurfaceSpec[] */ ]
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Screens
|
|
37
|
+
|
|
38
|
+
```jsonc
|
|
39
|
+
{
|
|
40
|
+
"id": "scr_welcome", // optional; generated from name when omitted
|
|
41
|
+
"name": "Welcome",
|
|
42
|
+
"containerStyle": { "backgroundFill": { "kind": "color", "color": "#0EA5E9" } },
|
|
43
|
+
"header": [ /* LayerIntent[] — back_button / progress chrome */ ],
|
|
44
|
+
"body": [ /* LayerIntent[] — required main content */ ],
|
|
45
|
+
"footer": [ /* LayerIntent[] — sticky CTAs */ ],
|
|
46
|
+
"next": "scr_goal" // jump target (scr_*/dec_*/surf_*) or null to end the flow
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`next` becomes the screen's `next.default`. Use `null` (or omit and rely on an
|
|
51
|
+
`end_flow` button) for terminal screens.
|
|
52
|
+
|
|
53
|
+
## Layer intents
|
|
54
|
+
|
|
55
|
+
Every entry in `header`/`body`/`footer` is a **layer intent** — a friendly shape
|
|
56
|
+
the scaffold expands into a real layer. `id` is always optional. `style` and
|
|
57
|
+
`styleBreakpoints` are passed through verbatim (validated by the layer schema).
|
|
58
|
+
|
|
59
|
+
### Text, image, icon, media
|
|
60
|
+
|
|
61
|
+
```jsonc
|
|
62
|
+
{ "kind": "text", "text": "Welcome to Rheo", "style": { "color": "#FFFFFF" } }
|
|
63
|
+
{ "kind": "text", "text": { "default": "Hi", "translations": { "es": "Hola" } } }
|
|
64
|
+
{ "kind": "image", "mediaAssetId": "00000000-0000-0000-0000-000000000101", "alt": "Hero" }
|
|
65
|
+
{ "kind": "icon", "iconName": "sparkles-outline" } // family defaults to ionicons
|
|
66
|
+
{ "kind": "lottie", "mediaAssetId": "…", "loop": true, "autoPlay": true }
|
|
67
|
+
{ "kind": "video", "mediaAssetId": "…", "autoPlay": false } // needs a play_media button
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Copy fields accept a plain string (becomes `{ default }`) or an explicit
|
|
71
|
+
`{ default, translations }`.
|
|
72
|
+
|
|
73
|
+
### Buttons, back buttons, links
|
|
74
|
+
|
|
75
|
+
The convenience fields `label`/`icon`/`labelStyle` are expanded into nested
|
|
76
|
+
`text`/`icon` children — you do not write `children` yourself unless you need
|
|
77
|
+
custom content. `action` defaults to `continue`. Source `variant` names
|
|
78
|
+
(`outline`, `text`, `link`, `default`, `filled`, `danger`) are mapped to the Rheo
|
|
79
|
+
enum automatically.
|
|
80
|
+
|
|
81
|
+
```jsonc
|
|
82
|
+
{ "kind": "button", "label": "Continue", "action": "continue", "variant": "primary",
|
|
83
|
+
"labelStyle": { "color": "#FFFFFF" } }
|
|
84
|
+
{ "kind": "button", "label": "Maybe later", "action": "skip", "variant": "ghost" }
|
|
85
|
+
{ "kind": "button", "label": "Go to plan", "action": { "kind": "go_to_step", "screenId": "scr_plan" } }
|
|
86
|
+
{ "kind": "back_button", "icon": "chevron-back-outline", "variant": "ghost" } // header chrome, no action
|
|
87
|
+
{ "kind": "hyperlink", "href": "https://rheo.app/terms", "label": "Terms" }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Action shorthands: `"none"`, `"continue"`, `"skip"`, `"end_flow"`,
|
|
91
|
+
`"go_back_one_screen"`, `"request_app_review"`. Object forms:
|
|
92
|
+
`{ "kind": "go_to_step", "screenId": "scr_x" }`,
|
|
93
|
+
`{ "kind": "request_os_permission", "permissionKey": "…", "outcomes": { "granted": "scr_a", "denied": "scr_b", "blocked": "scr_c" } }`,
|
|
94
|
+
`{ "kind": "play_media", "targetLayerIds": ["lyr_video"] }`.
|
|
95
|
+
|
|
96
|
+
### Stacks (layout)
|
|
97
|
+
|
|
98
|
+
```jsonc
|
|
99
|
+
{ "kind": "stack", "direction": "vertical", "align": "center", "gap": 12,
|
|
100
|
+
"style": { "padding": { "t": 24, "r": 16, "b": 24, "l": 16 } },
|
|
101
|
+
"children": [ { "kind": "text", "text": "Centered hero copy" } ] }
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Card chrome (border/shadow/background/radius) goes on a wrapping stack's `style`.
|
|
105
|
+
|
|
106
|
+
### Choices
|
|
107
|
+
|
|
108
|
+
You provide `options`; the scaffold builds the option `stack` children,
|
|
109
|
+
`optionBindings`, and `branching`. `optionId` defaults to a slug of the label.
|
|
110
|
+
Map unselected chrome to `style` and selected chrome to `selectedStyle` per option.
|
|
111
|
+
|
|
112
|
+
```jsonc
|
|
113
|
+
{
|
|
114
|
+
"kind": "single_choice",
|
|
115
|
+
"fieldKey": "gender",
|
|
116
|
+
"direction": "vertical",
|
|
117
|
+
"gap": 12,
|
|
118
|
+
"options": [
|
|
119
|
+
{ "optionId": "male", "label": "Male",
|
|
120
|
+
"style": { "border": { "width": 1, "color": "#E5E7EB" }, "radius": 12, "padding": { "t": 16, "r": 16, "b": 16, "l": 16 } },
|
|
121
|
+
"selectedStyle": { "border": { "width": 1, "color": "#6D5DF6" }, "background": "#6D5DF610" },
|
|
122
|
+
"labelStyle": { "color": "#0A0A0A" } },
|
|
123
|
+
{ "optionId": "female", "label": "Female", "labelStyle": { "color": "#0A0A0A" } }
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`multiple_choice` adds optional `minSelections`/`maxSelections`. To branch per
|
|
129
|
+
option, set `branching: { "enabled": true, "conditions": [{ "choiceId": "male", "goTo": "scr_x" }] }`.
|
|
130
|
+
|
|
131
|
+
### Inputs
|
|
132
|
+
|
|
133
|
+
```jsonc
|
|
134
|
+
{ "kind": "text_input", "fieldKey": "first_name", "placeholder": "Your name",
|
|
135
|
+
"inputType": "plain", "required": true, "classification": "safe" }
|
|
136
|
+
{ "kind": "scale_input", "fieldKey": "fitness_level", "min": 1, "max": 5, "step": 1,
|
|
137
|
+
"minLabel": "Beginner", "maxLabel": "Pro" }
|
|
138
|
+
{ "kind": "checkbox", "fieldKey": "accept_terms", "blocking": true }
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Use **at most one** input layer per screen, and add a `continue` button on
|
|
142
|
+
screens with `text_input`, `multiple_choice`, or `scale_input`.
|
|
143
|
+
|
|
144
|
+
### Auth
|
|
145
|
+
|
|
146
|
+
```jsonc
|
|
147
|
+
{ "kind": "oauth_login", "providers": [
|
|
148
|
+
{ "type": "preset", "provider": "apple" },
|
|
149
|
+
{ "type": "preset", "provider": "google" },
|
|
150
|
+
{ "type": "custom", "label": "SSO", "iconName": "key-outline", "buttonVariant": "secondary" }
|
|
151
|
+
] }
|
|
152
|
+
{ "kind": "email_password_auth", "fieldKey": "auth", "mode": "sign_up",
|
|
153
|
+
"submitLabel": "Create account", "minPasswordLength": 8 }
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Keep OAuth, email/password, and questionnaire inputs on **separate screens**.
|
|
157
|
+
|
|
158
|
+
### Carousel (swipe-only onboarding pager)
|
|
159
|
+
|
|
160
|
+
```jsonc
|
|
161
|
+
{
|
|
162
|
+
"kind": "carousel",
|
|
163
|
+
"pageControl": { "position": "bottom" },
|
|
164
|
+
"slides": [
|
|
165
|
+
{ "align": "center", "gap": 12, "children": [
|
|
166
|
+
{ "kind": "image", "mediaAssetId": "…" },
|
|
167
|
+
{ "kind": "text", "text": "Track your streak", "style": { "color": "#0A0A0A" } }
|
|
168
|
+
] },
|
|
169
|
+
{ "align": "center", "gap": 12, "children": [
|
|
170
|
+
{ "kind": "image", "mediaAssetId": "…" },
|
|
171
|
+
{ "kind": "text", "text": "Stay consistent", "style": { "color": "#0A0A0A" } }
|
|
172
|
+
] }
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
No buttons inside a carousel — paging is swipe-only ([carousel-import.md](carousel-import.md)).
|
|
178
|
+
|
|
179
|
+
### Counter / progress / loader
|
|
180
|
+
|
|
181
|
+
```jsonc
|
|
182
|
+
{ "kind": "progress", "fillColor": "#6D5DF6", "trackColor": "#E5E7EB" }
|
|
183
|
+
{ "kind": "counter", "startValue": 0, "endValue": 1000, "durationMs": 1200 }
|
|
184
|
+
{ "kind": "loader", "variant": "circular", "targetPercent": 100, "onComplete": { "mode": "next" } }
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Decisions and external surfaces
|
|
188
|
+
|
|
189
|
+
`decisions` accepts full `@getrheo/contracts` `DecisionNode` objects (the scaffold
|
|
190
|
+
passes them through). RevenueCat paywalls use `externalSurfaces`:
|
|
191
|
+
|
|
192
|
+
```jsonc
|
|
193
|
+
{
|
|
194
|
+
"id": "surf_paywall",
|
|
195
|
+
"provider": "revenuecat",
|
|
196
|
+
"offeringId": "default",
|
|
197
|
+
"presentation": "paywall",
|
|
198
|
+
"outcomes": { "purchase_completed": "scr_done", "restore_completed": "scr_done", "dismissed": "scr_offer2" },
|
|
199
|
+
"fallback": "scr_offer2" // required
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Run it
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
node scripts/scaffold-manifest.mjs ./rheo-import.flow-spec.json --out ./rheo-import.manifest.json
|
|
207
|
+
node scripts/validate-manifest.mjs ./rheo-import.manifest.json
|
|
208
|
+
node scripts/audit-publish-manifest.mjs ./rheo-import.manifest.json
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
If scaffold prints `scaffold_status=schema_valid`, the skeleton is structurally
|
|
212
|
+
correct. Then enrich `theme`/`style` from audit evidence and fix any
|
|
213
|
+
publish-gate blockers ([publish-gates.md](publish-gates.md)). A complete worked
|
|
214
|
+
spec lives at [../examples/flow-spec.example.json](../examples/flow-spec.example.json).
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Custom font import (rheo-import.fonts.json)
|
|
2
|
+
|
|
3
|
+
Fonts are **not** media assets. Never list `.ttf`, `.otf`, `.woff`, or `.woff2` files in `rheo-import.assets.json`.
|
|
4
|
+
|
|
5
|
+
## Two files, two jobs
|
|
6
|
+
|
|
7
|
+
| File | Purpose |
|
|
8
|
+
|------|---------|
|
|
9
|
+
| `rheo-import.assets.json` | Images, Lottie JSON, videos only (`media.mediaAssetId` placeholders) |
|
|
10
|
+
| `rheo-import.fonts.json` | Custom font files for app branding + `manifest.theme.fontFamily` |
|
|
11
|
+
|
|
12
|
+
Dashboard import **rejects** a font path in `rheo-import.assets.json` when `type` is `image` (or any type that does not match the extension).
|
|
13
|
+
|
|
14
|
+
## Copy paths
|
|
15
|
+
|
|
16
|
+
- Put font binaries under `assets/fonts/` in the ZIP (not mixed into image folders).
|
|
17
|
+
- Use a single `assets/` prefix in paths, e.g. `assets/fonts/CalSans-Regular.ttf` — avoid `assets/assets/fonts/...`.
|
|
18
|
+
|
|
19
|
+
## rheo-import.fonts.json shape
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"fontFamilies": [
|
|
24
|
+
{
|
|
25
|
+
"name": "CalSans",
|
|
26
|
+
"styles": [
|
|
27
|
+
{
|
|
28
|
+
"id": "00000000-0000-0000-0000-000000000501",
|
|
29
|
+
"weight": 400,
|
|
30
|
+
"italic": false,
|
|
31
|
+
"path": "assets/fonts/CalSans-Regular.ttf",
|
|
32
|
+
"filename": "CalSans-Regular.ttf"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- `name` — family string referenced by `manifest.theme.fontFamily` (e.g. `"CalSans"`).
|
|
41
|
+
- `styles[].path` — ZIP path to the font file (extension must match the binary).
|
|
42
|
+
- `styles[].id` — stable placeholder UUID per style (weight + italic), not used in manifest layers.
|
|
43
|
+
|
|
44
|
+
Font rows do **not** use `type` or `contentType`. Upload MIME is inferred from the file extension on import.
|
|
45
|
+
|
|
46
|
+
## Supported font MIME types (upload)
|
|
47
|
+
|
|
48
|
+
| Extension | MIME used on sign-upload |
|
|
49
|
+
|-----------|--------------------------|
|
|
50
|
+
| `.ttf` | `font/ttf` |
|
|
51
|
+
| `.otf` | `font/otf` |
|
|
52
|
+
| `.woff` | `font/woff` |
|
|
53
|
+
| `.woff2` | `font/woff2` |
|
|
54
|
+
|
|
55
|
+
## Common agent mistakes (BLOCKING)
|
|
56
|
+
|
|
57
|
+
1. **Font in `rheo-import.assets.json`** with `"type": "image"` and `image/png` — wrong file and wrong MIME.
|
|
58
|
+
2. **Font in `rheo-import.assets.json`** with `"type": "font"` — still wrong file; use `rheo-import.fonts.json` only.
|
|
59
|
+
3. **Only `manifest.theme.fontFamily`** without bundling font files — family name alone does not load the font.
|
|
60
|
+
4. **Duplicate path prefix** — `assets/assets/fonts/...` from joining paths incorrectly.
|
|
61
|
+
|
|
62
|
+
## Checklist before zipping
|
|
63
|
+
|
|
64
|
+
- [ ] Every `.ttf`/`.otf`/`.woff`/`.woff2` appears only in `rheo-import.fonts.json`, not in `rheo-import.assets.json`
|
|
65
|
+
- [ ] `manifest.theme.fontFamily` matches a `fontFamilies[].name`
|
|
66
|
+
- [ ] Each style `path` exists in the ZIP under `assets/fonts/`
|
|
67
|
+
- [ ] No font file uses `media.mediaAssetId` in the manifest
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Import Workflow
|
|
2
|
+
|
|
3
|
+
Generate a complete Rheo import from an existing app flow. If the source flow uses media, output a ZIP bundle. Raw JSON is acceptable only after an explicit asset audit finds no local media.
|
|
4
|
+
|
|
5
|
+
## Mandatory Intake Questionnaire (blocking)
|
|
6
|
+
|
|
7
|
+
Ask these questions **before** running the audit or generating a manifest. Record every answer in chat. Do not skip when the repo structure looks obvious.
|
|
8
|
+
|
|
9
|
+
1. Which file, route, or coordinator starts the existing flow?
|
|
10
|
+
2. What is the business purpose of this flow in one sentence?
|
|
11
|
+
3. Should the import optimize for **visual fidelity** or **editable structure** in Rheo?
|
|
12
|
+
4. If source code and screenshots disagree, which is more current?
|
|
13
|
+
5. Which steps must stay native/host-owned (signature pad, camera, custom WebView, paywall SDK) vs approximated in Rheo?
|
|
14
|
+
6. Match motion from the codebase (may differ slightly from Rheo presets)? (yes/no)
|
|
15
|
+
7. Does the app use i18n/localization? If yes, which locale is the default/fallback? All `text.default` values must be resolved strings from that locale — never raw keys.
|
|
16
|
+
|
|
17
|
+
If the user has not named an entry point, stop after question 1 and wait for an answer.
|
|
18
|
+
|
|
19
|
+
## Steps
|
|
20
|
+
|
|
21
|
+
1. Fetch the latest Rheo Manifest Agent Profile:
|
|
22
|
+
- Preferred raw docs URL: `/docs/md/developer-guide/agent-manifest-profile`.
|
|
23
|
+
- If fetching fails, read [manifest-agent-profile-fallback.md](manifest-agent-profile-fallback.md).
|
|
24
|
+
- Record the profile version used.
|
|
25
|
+
2. Complete the mandatory intake questionnaire above. Do not proceed without answers.
|
|
26
|
+
3. Run the bundled audit before generating — **from the target app root, not the skill folder**:
|
|
27
|
+
- `RHEO=/abs/path/to/rheo/rheo-flow-import` then `cd <target-app>`.
|
|
28
|
+
- `node "$RHEO/scripts/audit-import.mjs" --entry <flow-entry> --out rheo-import.audit.md`
|
|
29
|
+
- `--entry` is the flow's entry **file** (import graph is traced) or a **directory** (source subtree only). Repeat `--entry` for multiple roots, or comma-separate: `--entry a.tsx,b.tsx`. The audit does **not** scan the whole repo.
|
|
30
|
+
- Read the printed `audit_root` / `audit_entries` / `audit_scope` / `audit_scanned_files`. If `audit_root` is not the host app (e.g. it points inside the skill), fix `--entry`/`--root` and re-run — do not trust an audit rooted in the skill.
|
|
31
|
+
- When intake Q6 is **yes**, add `--suggest-animations rheo-import.animations.json`.
|
|
32
|
+
- Use the audit report as the evidence source for regions, styles, backgrounds, assets, and motion.
|
|
33
|
+
- If the audit cannot run, explain why and manually produce the same report sections.
|
|
34
|
+
4. Identify the host stack: React Native / Expo, SwiftUI, or best-effort other mobile stack.
|
|
35
|
+
5. Find the flow entry point:
|
|
36
|
+
- Ask for the file/route/coordinator if the user did not provide it.
|
|
37
|
+
- React Native examples: Expo Router path, React Navigation screen, `OnboardingScreen`.
|
|
38
|
+
- SwiftUI examples: `NavigationStack` root, onboarding view, coordinator push.
|
|
39
|
+
6. Trace reachable steps:
|
|
40
|
+
- Follow imports and navigation calls.
|
|
41
|
+
- Extract UI strings, button labels, input field keys, paywall calls, auth calls, permission prompts, links, and terminal navigation.
|
|
42
|
+
- When the audit reports localization ([localization-import.md](localization-import.md)): resolve `t('key')` / `formatMessage` ids from the **default locale** JSON (e.g. `locales/en.json`), not the key itself. Set `manifest.defaultLocale` to that locale.
|
|
43
|
+
- Detect branches from conditionals, feature flags, platform checks, or stored user state.
|
|
44
|
+
7. Infer screen regions from audit findings:
|
|
45
|
+
- Look for top bars, safe-area header containers, `Header`, `Toolbar`, back icons, close buttons, and progress indicators.
|
|
46
|
+
- Put back/close controls and flow progress into `regions.header` when they appear above main content.
|
|
47
|
+
- Look for sticky CTA containers, `Footer`, bottom safe-area wrappers, and buttons outside scroll/content containers.
|
|
48
|
+
- Put persistent bottom CTAs into `regions.footer`, not at the bottom of `regions.body`.
|
|
49
|
+
- Use `regions.body` for scrollable or main content.
|
|
50
|
+
8. Extract style tokens from audit findings:
|
|
51
|
+
- Inspect theme files, design-token files, Tailwind/NativeWind config, CSS variables, `StyleSheet.create`, shared component variants, and button/text primitives.
|
|
52
|
+
- Map primary/background/foreground/accent colors into `manifest.theme` when clearly available.
|
|
53
|
+
- Reuse obvious font sizes, font weights, spacing, border radius, and CTA variants from source components.
|
|
54
|
+
- Set `style.color` on text layers when screens use dark/saturated backgrounds (`bg-red-600`, `text-white`, etc.).
|
|
55
|
+
- Prefer the app’s named tokens over arbitrary black-and-white defaults.
|
|
56
|
+
- Black-and-white fallback is allowed only when the audit finds no color evidence **and** the user confirms there is no theme file.
|
|
57
|
+
9. Map screen backgrounds and gradients:
|
|
58
|
+
- For `BackgroundGradient`, `LinearGradient`, or `expo-linear-gradient`, set `screen.containerStyle.backgroundFill` to `{ "kind": "color", "color": "<linear-gradient CSS>" }`.
|
|
59
|
+
- Example: `linear-gradient(180deg, #CCFBF1 0%, #F5F5F4 100%)` for a teal-to-stone shell gradient.
|
|
60
|
+
- When a screen overrides with a solid brand color (e.g. red education pager), use that hex on the screen and white text.
|
|
61
|
+
- Do not put gradients only in `manifest.theme.background` — theme background is a flat default, not a per-screen gradient.
|
|
62
|
+
10. Map carousels and in-screen pagers (see [carousel-import.md](carousel-import.md)):
|
|
63
|
+
- When source uses `infoSteps`, `currentInfoStep`, horizontal `translateX` pagers, or `pagingEnabled` lists, emit `kind: "carousel"`.
|
|
64
|
+
- Each slide is a vertical `stack` with image, title, and body text from that slide.
|
|
65
|
+
- Add `pageControl: { "position": "bottom" }` when dot indicators exist (dots only).
|
|
66
|
+
- Carousels are **swipe-only** — no Next/Continue button on the carousel layer. Do not add footer/body buttons for in-pager paging.
|
|
67
|
+
- Use `regions.footer` only when the source CTA advances the **next screen in the flow**, or for single-slide carousels that need an explicit Continue.
|
|
68
|
+
- Bundle every slide image; every asset referenced in the carousel must appear in `rheo-import.assets.json`.
|
|
69
|
+
- Do not collapse multi-slide routes into one static screen with a single image.
|
|
70
|
+
11. Map layout, alignment, borders, and shadows:
|
|
71
|
+
- Parent vertical stacks that center content need `align: "center"` (and `justify: "center"` when appropriate).
|
|
72
|
+
- Hero images belong inside centered stacks, not as lone siblings in an unaligned vertical stack.
|
|
73
|
+
- Card rows (rating, testimonials, why-it-works cards) use wrapping stacks with `style.background`, `style.radius`, `style.padding`, `style.border`, and `style.shadow` from source.
|
|
74
|
+
12. Map custom fonts from audit findings ([font-import.md](font-import.md)):
|
|
75
|
+
- Detect `Font.loadAsync`, `useFonts`, Tailwind `fontFamily`, and `font-*` classes tied to loaded families.
|
|
76
|
+
- Copy every `.ttf`, `.otf`, `.woff`, and `.woff2` file into `assets/fonts/` in the ZIP (path like `assets/fonts/CalSans-Regular.ttf`).
|
|
77
|
+
- Write `rheo-import.fonts.json` with stable placeholder UUIDs per font style (weight + italic).
|
|
78
|
+
- Set `manifest.theme.fontFamily` to the primary family name used in source (e.g. `CalSans`).
|
|
79
|
+
- **Do not** add font files to `rheo-import.assets.json` — not as `type: "image"`, not as `type: "font"`. Fonts are not `media.mediaAssetId` assets.
|
|
80
|
+
- Dashboard import uploads fonts and merges them into app branding; the manifest only references the family name string.
|
|
81
|
+
13. Map choice option default and selected states:
|
|
82
|
+
- For `single_choice` and `multiple_choice`, each selectable option is a child `stack` layer.
|
|
83
|
+
- Map unselected Tailwind/classes to `style` on that stack (border, background, padding, radius).
|
|
84
|
+
- Map selected classes to `selectedStyle` on the same stack — the renderer merges selected over default.
|
|
85
|
+
- When label text color changes on selection, set default `style.color` on the nested text layer and selected color via `selectedStyle` on the stack or nested text as appropriate.
|
|
86
|
+
- Audit flags `selected === … ? … : …` ternaries — do not ship choice screens with only default styling.
|
|
87
|
+
14. Extract assets from audit findings:
|
|
88
|
+
- Follow static imports, `require(...)`, image maps, Lottie JSON imports, video imports, and asset constants used by traced screens.
|
|
89
|
+
- Inspect shared components and design primitives too; hero illustrations and logos are often hidden behind `Illustration`, `Logo`, `Avatar`, `Mascot`, or `ImageBackground` components.
|
|
90
|
+
- Inspect platform asset catalogs and config references where relevant, for example Expo `assets/`, React Native image folders, Swift asset catalogs, and bundled Lottie/video directories.
|
|
91
|
+
- Copy referenced local assets into an `assets/` folder in the output bundle.
|
|
92
|
+
- For each asset, generate a stable placeholder UUID and use that UUID as `media.mediaAssetId` in the manifest.
|
|
93
|
+
- Write `rheo-import.assets.json` mapping placeholder UUIDs to bundled file paths.
|
|
94
|
+
- If a referenced asset cannot be copied, stop and report the missing asset instead of silently dropping the media layer.
|
|
95
|
+
15. Ask targeted follow-up questions for audit ambiguity:
|
|
96
|
+
- Ask only when ambiguity changes the manifest materially.
|
|
97
|
+
- Continue with low-confidence review notes when not blocked.
|
|
98
|
+
- Examples: shared header vs per-step header, exact gradient mapping, light/dark asset choice, conflicting button token sources.
|
|
99
|
+
16. Use screenshots/recordings if supplied:
|
|
100
|
+
- Use them for visual hierarchy, copy confirmation, colors, media placement, and step order.
|
|
101
|
+
- Do not override clear source behavior unless the user says the visual artifact is newer.
|
|
102
|
+
17. Build a private flow model:
|
|
103
|
+
- ordered screens
|
|
104
|
+
- branches
|
|
105
|
+
- inputs
|
|
106
|
+
- external surfaces
|
|
107
|
+
- terminal outcomes
|
|
108
|
+
- unmappable native behavior
|
|
109
|
+
18. Generate `rheo-import.manifest.json`:
|
|
110
|
+
- Emit a complete graph with explicit `next.default` edges.
|
|
111
|
+
- Use decision nodes for clear branches.
|
|
112
|
+
- Use RevenueCat external surfaces for detected RevenueCat paywalls.
|
|
113
|
+
- Prefer fidelity when the user chose visual fidelity: carousels, gradients, shadows, and centered stacks.
|
|
114
|
+
- **Layer ids:** `scr_*` / `lyr_*` only on screens and layers; UUID placeholders **only** for `media.mediaAssetId` and font sidecars ([layer-schema-pitfalls.md](layer-schema-pitfalls.md#layer-ids-vs-media-placeholder-uuids)).
|
|
115
|
+
- **Container layers:** every `button`, `back_button`, `hyperlink`, `stack`, choice input, and auth layer must include its `children` (or `slides`) array ([manifest-rules.md](manifest-rules.md#container-layers-required-children)).
|
|
116
|
+
- **Header back buttons:** `kind: "back_button"`, Rheo `variant` enum only (map source `outline`/`text`/`link`), nested `icon`/`text` children, **no `action`**, `icon.family: "ionicons"` ([layer-schema-pitfalls.md](layer-schema-pitfalls.md#back_button-header-chrome)).
|
|
117
|
+
- **Choice screens:** every `single_choice` / `multiple_choice` includes `fieldKey`, `children` (≥2 option stacks), `optionBindings`, and `branching` — never `"options"` arrays ([layer-schema-pitfalls.md](layer-schema-pitfalls.md#single_choice--multiple_choice)).
|
|
118
|
+
- When intake Q6 is **yes** and the workspace plan includes animations, apply motion from [animation-import.md](animation-import.md) (audit presets, stagger, conservative `restingMotion`). Omit motion when Q6 is **no** or plan lacks animations.
|
|
119
|
+
19. Validate and publish gate audit:
|
|
120
|
+
- Run `node "$RHEO/scripts/validate-manifest.mjs" ./rheo-import.manifest.json` (schema + dashboard publish rules).
|
|
121
|
+
- Run `node "$RHEO/scripts/audit-publish-manifest.mjs" ./rheo-import.manifest.json` and read `rheo-import.publish-gates.md`.
|
|
122
|
+
- Fix every **blocking** issue (especially missing `style.color` on text and button labels, missing Continue on input screens, entry/completion path).
|
|
123
|
+
- See [publish-gates.md](publish-gates.md) for the full checklist mirrored from the builder **Publish** button.
|
|
124
|
+
- If safe normalization is needed, run `node "$RHEO/scripts/normalize-manifest.mjs" ./rheo-import.manifest.json`, then re-run validate + publish audit.
|
|
125
|
+
20. Bundle:
|
|
126
|
+
- If there are no assets or fonts, provide `rheo-import.manifest.json` and explicitly state `no local assets found`.
|
|
127
|
+
- If there are assets or fonts, create `rheo-import.zip` containing `rheo-import.manifest.json`, optional `rheo-import.assets.json`, optional `rheo-import.fonts.json`, and files under `assets/`.
|
|
128
|
+
- Do not finish with JSON-only output when any traced screen references local image, Lottie, video, or font files.
|
|
129
|
+
|
|
130
|
+
## Output
|
|
131
|
+
|
|
132
|
+
Primary artifact:
|
|
133
|
+
|
|
134
|
+
- No assets after explicit audit: `rheo-import.manifest.json`.
|
|
135
|
+
- With assets: `rheo-import.zip`.
|
|
136
|
+
|
|
137
|
+
`rheo-import.assets.json` — **images, Lottie, and video only** (never fonts):
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"assets": [
|
|
142
|
+
{
|
|
143
|
+
"id": "00000000-0000-0000-0000-000000000101",
|
|
144
|
+
"path": "assets/hero.png",
|
|
145
|
+
"type": "image",
|
|
146
|
+
"contentType": "image/png",
|
|
147
|
+
"name": "hero.png"
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Supported `rheo-import.assets.json` MIME types (dashboard import rejects others):**
|
|
154
|
+
|
|
155
|
+
| `type` | `contentType` values | Extensions |
|
|
156
|
+
|--------|----------------------|------------|
|
|
157
|
+
| `image` | `image/png`, `image/jpeg`, `image/webp`, `image/gif`, `image/svg+xml` | `.png`, `.jpg`, `.jpeg`, `.webp`, `.gif`, `.svg` |
|
|
158
|
+
| `lottie` | `application/json` | `.json` |
|
|
159
|
+
| `video` | `video/mp4`, `video/webm`, `video/quicktime` | `.mp4`, `.webm`, `.mov` |
|
|
160
|
+
|
|
161
|
+
Do **not** put `.ttf`, `.otf`, `.woff`, or `.woff2` in this file — use `rheo-import.fonts.json` ([font-import.md](font-import.md)). Do **not** bundle HEIC or AVIF as images. Match `type` and `contentType` to the file extension (do not label a `.json` Lottie as `type: "image"`).
|
|
162
|
+
|
|
163
|
+
`rheo-import.fonts.json` — **custom fonts only** (separate from assets.json):
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"fontFamilies": [
|
|
168
|
+
{
|
|
169
|
+
"name": "CalSans",
|
|
170
|
+
"styles": [
|
|
171
|
+
{
|
|
172
|
+
"id": "00000000-0000-0000-0000-000000000501",
|
|
173
|
+
"weight": 400,
|
|
174
|
+
"italic": false,
|
|
175
|
+
"path": "assets/fonts/CalSans-Regular.ttf",
|
|
176
|
+
"filename": "CalSans-Regular.ttf"
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Choice option stack example (each option child of `single_choice` / `multiple_choice`):
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"id": "lyr_goal_a",
|
|
189
|
+
"kind": "stack",
|
|
190
|
+
"style": {
|
|
191
|
+
"border": { "width": 1, "color": "#E5E7EB" },
|
|
192
|
+
"background": "#F9FAFB",
|
|
193
|
+
"radius": 12,
|
|
194
|
+
"padding": 16
|
|
195
|
+
},
|
|
196
|
+
"selectedStyle": {
|
|
197
|
+
"border": { "width": 1, "color": "#6D5DF6" },
|
|
198
|
+
"background": "#6D5DF610"
|
|
199
|
+
},
|
|
200
|
+
"children": [
|
|
201
|
+
{
|
|
202
|
+
"id": "lyr_goal_a_text",
|
|
203
|
+
"kind": "text",
|
|
204
|
+
"text": { "default": "Option A" },
|
|
205
|
+
"style": { "color": "#0A0A0A" }
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Wrap option stacks in a full choice layer — see [layer-schema-pitfalls.md](layer-schema-pitfalls.md#single_choice--multiple_choice) for the required `fieldKey`, `optionBindings`, and `branching` wrapper.
|
|
212
|
+
|
|
213
|
+
Mention review notes in chat, not inside the manifest, unless Rheo contracts support the metadata.
|
|
214
|
+
|
|
215
|
+
## Completion Gate
|
|
216
|
+
|
|
217
|
+
Before final response, answer these for yourself:
|
|
218
|
+
|
|
219
|
+
- Did I complete the mandatory intake questionnaire and record answers in chat?
|
|
220
|
+
- Did I run audit with `--entry` and use carousel/style/background/layout/font/choice findings?
|
|
221
|
+
- Did I bundle custom fonts in `rheo-import.fonts.json` only (no `.ttf`/`.otf`/`.woff` rows in `rheo-import.assets.json`) and set `manifest.theme.fontFamily`?
|
|
222
|
+
- If the app uses i18n, did every `text.default` use resolved default-locale copy (not translation keys)?
|
|
223
|
+
- Did every choice option stack include both `style` and `selectedStyle` when the audit flagged selected ternaries?
|
|
224
|
+
- Did I emit `carousel` layers for every multi-slide route the audit flagged without duplicating pager buttons in `regions.footer`?
|
|
225
|
+
- Did I apply gradients to `screen.containerStyle.backgroundFill` and set text colors on colored screens?
|
|
226
|
+
- Did I center images and map card borders/shadows to wrapping stacks?
|
|
227
|
+
- Did I inspect source asset references from every traced screen and shared visual component?
|
|
228
|
+
- Did I either create `rheo-import.zip` or explicitly confirm no local assets were found?
|
|
229
|
+
- Does every `media.mediaAssetId` refer to a placeholder UUID listed in `rheo-import.assets.json` when assets exist?
|
|
230
|
+
- Does the ZIP include every file listed in `rheo-import.assets.json`?
|
|
231
|
+
- Did publish gate audit pass with zero blocking issues?
|
|
232
|
+
- Did every `back_button`, `button`, and `hyperlink` layer include a `children` array with nested label content (not omitted)?
|
|
233
|
+
- Did every layer use `lyr_*` ids (no asset UUIDs as layer ids)?
|
|
234
|
+
- Did every `back_button` use a Rheo `variant` enum value and omit `action`?
|
|
235
|
+
- Did every `single_choice` / `multiple_choice` include `fieldKey`, `optionBindings`, and `branching`?
|
|
236
|
+
- Did `node "$RHEO/scripts/validate-manifest.mjs"` exit 0 before zipping?
|
|
237
|
+
|
|
238
|
+
If any answer is no, the import is not complete.
|
|
239
|
+
|
|
240
|
+
Also verify that high-confidence audit findings were either used in the manifest/ZIP or explicitly explained in review notes.
|