@feastalytics/cli 0.1.3 → 0.1.5
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/dist/cli.js +5668 -588
- package/feast/SKILL.md +9 -1
- package/feast/references/links.md +80 -0
- package/feast/references/workflows.md +29 -9
- package/package.json +1 -1
package/feast/SKILL.md
CHANGED
|
@@ -91,7 +91,15 @@ For the domain-specific meaning of fields — how automations chain, what a funn
|
|
|
91
91
|
|
|
92
92
|
Many tasks are multi-step and have a required ordering the app normally enforces. The most important rule: **automations live inside flows — always find a flow (`listAutomationFlows`) or create one (`createAutomationFlow`) before adding automations; never create an orphan automation.** The same "resolve the parent/ids first, then act" shape recurs across campaigns, funnels, and offers.
|
|
93
93
|
|
|
94
|
-
For the ordered steps and domain rules of each common workflow, read `references/workflows.md`. It covers what's **fully doable** — creating/cloning a campaign, authoring automations end-to-end (
|
|
94
|
+
For the ordered steps and domain rules of each common workflow, read `references/workflows.md`. It covers what's **fully doable** — creating/cloning a campaign, authoring automations end-to-end (draft-first: stage edits on a draft, dry-run them with `simulateAutomations`, hand the user the preview link, then promote), editing funnel screens (the draft → preview → promote loop, including staging brand-new screens), reading and saving the wallet pass configuration (full-document save — read, modify, save the whole thing), listing/creating members-program rewards, creating offers, and exploring users — and what's **not yet exposed** — reward update/delete, pass image generation, brand identity, and replying to guests by SMS. Don't fabricate a call for a workflow whose tools aren't listed by `feast tools`; tell the user that part isn't available yet.
|
|
95
|
+
|
|
96
|
+
## Link to what you touched
|
|
97
|
+
|
|
98
|
+
Work you do through the CLI lands somewhere in the product, and a link is a cheap thing to offer — so offer them freely. After a turn where you created, changed, or published something, close with a short markdown list: where to see it, where to edit it, where to preview it. Not because anyone has to go check your work, but because opening the thing is usually the next step anyway. When someone asks where a thing lives or how to set it up, lead with the link rather than click-by-click directions.
|
|
99
|
+
|
|
100
|
+
**You don't know these URLs — read `references/links.md` before you write one.** The dashboard's shape is not the one you'd extrapolate from the guest-facing links elsewhere in this skill, so a URL that looks obviously right is the exact case to check. A wrong link is worse than no link: it looks authoritative and 404s.
|
|
101
|
+
|
|
102
|
+
That file has the dashboard routes with their panel and tab names, the guest-facing pages on the organization's own subdomain, the preview route that completes the funnel draft loop, and which query params actually suppress analytics versus merely tagging a visit as a preview.
|
|
95
103
|
|
|
96
104
|
## Worked example
|
|
97
105
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Linking to your work
|
|
2
|
+
|
|
3
|
+
Most of what you create or change through the CLI has a stable URL in the product. Handing one over is cheap and saves the user hunting through the dashboard for the thing you just made — opening it is usually their next step anyway.
|
|
4
|
+
|
|
5
|
+
So offer links freely: after a turn where you created, changed, or published something, close with a short markdown list — usually two to four, covering where to see it, where to edit it, and where to preview it. When someone asks where a thing lives or how to set it up, lead with the link rather than describing where to click. It's an affordance, not a checkpoint; nobody has to go verify your work.
|
|
6
|
+
|
|
7
|
+
You can build almost every URL below from ids you already have. `<organizationId>` is the same value you pass to `--org`. Campaign, flow and draft ids come back from the tool call you just made. Only `<subdomain>` needs a lookup.
|
|
8
|
+
|
|
9
|
+
Angle brackets below mark a value **you** substitute. A finished link contains no brackets, no braces and no backticks — if you emit `{{...}}` or a bare `<campaignId>`, the link is broken.
|
|
10
|
+
|
|
11
|
+
## Dashboard
|
|
12
|
+
|
|
13
|
+
Everything an authenticated user sees hangs off `https://feastalytics.com/<organizationId>/app`. Note the shape: the organization id is a **path segment**, not a subdomain — there is no `app.feastalytics.com`.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
/ home
|
|
17
|
+
/campaigns every acquisition campaign
|
|
18
|
+
/campaigns/<campaignId> one campaign
|
|
19
|
+
/campaigns/<campaignId>?panel=funnel-v2 that campaign's funnel editor
|
|
20
|
+
/members-program?panel=<panel> the members program
|
|
21
|
+
/members-program?panel=funnel the members program funnel editor
|
|
22
|
+
/automation/<flowId> one automation flow
|
|
23
|
+
/settings/<tab> settings
|
|
24
|
+
/chats guest conversations
|
|
25
|
+
/orders /activity /guest-journey
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Campaign panels: `overview`, `funnel-v2`, `automations`, `promotions`, `metrics`, `ads`, `orders`, `reservations`, `subscriptions`, `creative-strategy`. With no `?panel=`, an unpublished campaign opens on `funnel-v2` and a published one on `overview`.
|
|
29
|
+
|
|
30
|
+
Members-program panels: `overview`, `funnel`, `automations`, `pass-builder`, `rewards`.
|
|
31
|
+
|
|
32
|
+
Settings tabs: `account`, `general`, `members`, `integrations`, `notifications`, `usage`, `scanning`, `texting`, `subscription`. Point people at `/settings/integrations` when a task needs a POS or Meta connection you can't make for them.
|
|
33
|
+
|
|
34
|
+
Funnels are always edited inside a panel, never on a page of their own — a campaign's `funnel-v2` panel, or the members program's `funnel` panel.
|
|
35
|
+
|
|
36
|
+
### Automation previews
|
|
37
|
+
|
|
38
|
+
These two hang off the **root**, not off `/<organizationId>/app` — the organization id is the first path segment:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
https://feastalytics.com/automation-preview/<organizationId>/<flowId>
|
|
42
|
+
https://feastalytics.com/automation-preview/<organizationId>/<flowId>?draftId=<draftId>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Without `draftId` it dry-runs the live flow as a text-message thread. With one, the same page diffs the draft's staged changes against live — added messages tinted, removed struck through, edited showing the old copy above the new — which is the link to hand someone before you promote.
|
|
46
|
+
|
|
47
|
+
A `flowId` contains `:` and `;` and **must be percent-encoded** in the path. Easier: `createAutomationDraft` and `stageAutomationEdits` both return `previewUrls`, already built and encoded, one per flow the draft touches. Use those rather than assembling your own.
|
|
48
|
+
|
|
49
|
+
## Public pages
|
|
50
|
+
|
|
51
|
+
The guest-facing site lives on the organization's own subdomain, `https://<subdomain>.feastalytics.com`:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
/campaign/<campaignId> the live campaign funnel
|
|
55
|
+
/ the live members program funnel
|
|
56
|
+
/preview/<draftId> an unsaved funnel draft, as a tree of every screen
|
|
57
|
+
/preview/<draftId>/<campaignId> the same draft, scoped to one campaign
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Get `<subdomain>` from `loadCurrentOrganization` → `organization.subdomains2[].subdomain`. When the link is about a campaign, pick the subdomain matching that campaign's `referrers` rather than the first one. The funnel draft tools (`createFunnelDraft`, `getFunnelDraft`) already return the draft's `referrer`, so use that instead of looking it up again.
|
|
61
|
+
|
|
62
|
+
The `/preview/<draftId>` route is the payoff of the draft → preview → promote loop in `workflows.md`: it renders every screen of the staged funnel as a tree, so it's the right link to hand over after `stageFunnelEdit` and before `saveFunnelEdits`. It stops working once the draft is discarded or expires.
|
|
63
|
+
|
|
64
|
+
## Two query params that don't do what they look like
|
|
65
|
+
|
|
66
|
+
`utm_source=PREVIEW` only *tags* a visit as a preview so it can be filtered out of reporting later. The visit is still counted. The in-app preview panes append it, so use it when you want to match what the app does.
|
|
67
|
+
|
|
68
|
+
`internal_qa=1` is what actually suppresses analytics, and it sticks for that browser until cleared with `internal_qa=0`. Use it when the point is to look at a live page without being counted. `qa=1`, `review=1` and `campaign_review=1` suppress the current page only.
|
|
69
|
+
|
|
70
|
+
## Worked example
|
|
71
|
+
|
|
72
|
+
After creating a campaign and applying a funnel template. Every id below is substituted — this is what a finished message looks like, with nothing left to fill in:
|
|
73
|
+
|
|
74
|
+
```markdown
|
|
75
|
+
Done — "Fall Prix Fixe" is live as a draft.
|
|
76
|
+
|
|
77
|
+
- [Open the campaign](https://feastalytics.com/3e8cb27c-6e54-444b-859f-66dbae0e711b/app/campaigns/e8ccc852-6555-4a9a-b48b-127d687bb34a)
|
|
78
|
+
- [Edit the funnel](https://feastalytics.com/3e8cb27c-6e54-444b-859f-66dbae0e711b/app/campaigns/e8ccc852-6555-4a9a-b48b-127d687bb34a?panel=funnel-v2)
|
|
79
|
+
- [See it as a guest](https://melbourneseafoodstation.feastalytics.com/campaign/e8ccc852-6555-4a9a-b48b-127d687bb34a?utm_source=PREVIEW)
|
|
80
|
+
```
|
|
@@ -39,18 +39,26 @@ The one-shot text→campaign endpoints (`createWithOffer` / `parseCampaignDescri
|
|
|
39
39
|
- A **flow** groups automations by a shared trigger, and belongs to *either* a campaign *or* the members program — never both.
|
|
40
40
|
- **The rule that matters most: every automation needs a `flowId`. `batchEditAutomations` throws on a create op without one.** So you must resolve the flow *before* creating. Never invent a flowId.
|
|
41
41
|
|
|
42
|
-
### The CLI loop
|
|
42
|
+
### The CLI loop: draft → stage → share → save
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
Automations have a staging tier, and it is the default path. Changes accumulate on a **draft** — an off-prod overlay — until someone explicitly promotes them. A draft carries a link that renders the change as a text-message thread with the edits highlighted, which is what you hand a human to look at before anything reaches a real guest.
|
|
45
45
|
|
|
46
46
|
1. `listAutomationFlows` — find an existing flow. Pass `{ "campaignId": "<id>" }` for a campaign's flows, or `{ "scope": "membersProgram" }` for members-program flows. Reuse a matching flow when one fits.
|
|
47
47
|
2. If none fits, `createAutomationFlow` to make one. If the campaign/members-program has **no flows at all**, strongly prefer `applyAutomationTemplate` (then customize) over building from scratch. Only apply a template when there are no existing flows.
|
|
48
48
|
3. `listAutomations` with `{ "flowId": "<id>" }` to see the automations already in that flow before editing (omit the input to list every automation in the org).
|
|
49
|
-
4. `
|
|
50
|
-
|
|
49
|
+
4. `createAutomationDraft` with a short `title` describing the change in the user's terms ("Shorten the day-3 nudge") — that title is what the reviewer sees. Keep the returned `draftId`; **there is no way to list drafts, so if you lose it the draft is unreachable.**
|
|
50
|
+
5. `stageAutomationEdits` with `{ "draftId": "<id>", "operations": [...] }`. The ops are exactly the ones `batchEditAutomations` takes:
|
|
51
|
+
- `{ "type": "create", "automation": { ...full automation..., "flowId": "<id>" } }` — generate a fresh UUID for the automation's id, set the `flowId`, and include triggers, conditions, actions, send time, and a descriptive title all at once. Create ops require the flowId.
|
|
51
52
|
- `{ "type": "update", "automationId": "<id>", "automation": { ...changed fields... } }`
|
|
52
|
-
- `{ "type": "delete", "automationId": "<id>" }` — blocked if the automation already has sends.
|
|
53
|
-
|
|
53
|
+
- `{ "type": "delete", "automationId": "<id>" }` — blocked at save time if the automation already has sends.
|
|
54
|
+
Call it repeatedly to build a change up; ops append in order.
|
|
55
|
+
6. `simulateAutomations` with `{ "flowId": "<id>", "edits": <the draft's operations> }` — dry-run against a synthetic event timeline with **no real sends** and confirm the right automations fire. If the simulation surprises you, stage a correction rather than promoting and patching live.
|
|
56
|
+
7. **Give the user the `previewUrls` from the draft** and let them look before you promote. Each entry is one flow's before/after view. Don't promote unprompted work on the user's behalf — staging exists so a human sees the change first.
|
|
57
|
+
8. `saveAutomationEdits` with `{ "draftId": "<id>" }` — this is the write to production. It refuses if any automation the draft touches was changed by someone else since you staged, naming which; re-stage against the current state rather than retrying.
|
|
58
|
+
|
|
59
|
+
`discardAutomationDraft` throws a draft away without promoting. `getAutomationDraft` re-reads one by id. Drafts expire after 14 days.
|
|
60
|
+
|
|
61
|
+
**`batchEditAutomations` still exists and writes straight to production in one call.** Reach for it only when the user explicitly wants an immediate live change and has said so — not as a shortcut past the review step.
|
|
54
62
|
|
|
55
63
|
`updateAutomationFlow` renames/retitles a flow; `deleteAutomationFlow` removes a flow and its automations (blocked at ≥20 sends — turn it off instead).
|
|
56
64
|
|
|
@@ -142,8 +150,19 @@ Every offer picks one **framework**:
|
|
|
142
150
|
The retention counterpart to campaigns — flows with no `campaignId`.
|
|
143
151
|
|
|
144
152
|
- **Automations are authorable** (see the automations workflow): use `listAutomationFlows` with `{ "scope": "membersProgram" }`, then the same create/edit loop. Remember the members-program 30-day `awardReward` default.
|
|
145
|
-
- **
|
|
146
|
-
- **
|
|
153
|
+
- **Rewards are now readable and creatable.** `listMembersProgramRewards` returns every reward with its catalog `name`, `staffInstructions`, and `pointsCost` (fields are `null` when unset). `createMembersProgramReward` takes `{ "name", "staffInstructions"?, "pointsCost"? }` and does the catalog-item dance for you: an existing catalog item with that exact name is reused (updating its `staffInstructions` if you passed any), otherwise one is created, then the reward row is linked to it.
|
|
154
|
+
- **The `pointsCost` fork matters.** A reward with `pointsCost` set is redeemed *by the member with points* and is never auto-awarded. Omit `pointsCost` for automation-awarded rewards — and then actually pair the reward with an `awardReward` automation (see the automations workflow), or it will never reach anyone. `listMembersProgramRewards` deliberately has no `hasAutomation` flag: cross-reference `listAutomations` for `awardReward` actions carrying the reward's `itemId` to find orphans.
|
|
155
|
+
- Reward **update/delete are not exposed** yet — those still need the app.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Wallet pass configuration
|
|
160
|
+
|
|
161
|
+
The pass (the wallet membership card) is now CLI-readable and -writable as a whole document.
|
|
162
|
+
|
|
163
|
+
- **`readPassConfiguration`** `{}` — returns the latest live configuration: `sections`, `features`, `locations`, `metadata`, and its `version`.
|
|
164
|
+
- **`savePassConfiguration`** — **a full-document save, not a patch.** Anything you omit is dropped from the new version. The only safe workflow is read → modify the returned document → save the complete result. Saving appends a new version (history is preserved server-side), and a visible change triggers a re-push of the pass to every member's wallet — so this confirms before running and deserves the same care as a live send.
|
|
165
|
+
- Pass **image generation** (punch-card strips etc.) is not exposed — image workflows still need the app.
|
|
147
166
|
|
|
148
167
|
---
|
|
149
168
|
|
|
@@ -160,7 +179,7 @@ The retention counterpart to campaigns — flows with no `campaignId`.
|
|
|
160
179
|
|
|
161
180
|
**Applying a funnel template** expands a whole screen tree server-side in one call: `chooseFunnelTemplate` (needs the campaign's funnel unset — a fresh campaign — and resolves the referrer from the campaign). `deleteFunnel` tears one down.
|
|
162
181
|
|
|
163
|
-
**Editing individual funnel screens is now CLI-drivable** through a **draft → preview → promote** loop. You never apply edits locally: you stage them on an off-prod draft, preview the result at a stable URL, then save. Tools: `listFunnelScreens`, `createFunnelDraft`, `stageFunnelEdit`, `getFunnelDraft`, `listFunnelDrafts`, `discardFunnelDraft`, `saveFunnelEdits`.
|
|
182
|
+
**Editing individual funnel screens is now CLI-drivable** through a **draft → preview → promote** loop. You never apply edits locally: you stage them on an off-prod draft, preview the result at a stable URL, then save. Tools: `listFunnelScreens`, `createFunnelDraft`, `stageFunnelEdit`, `stageFunnelScreen`, `getFunnelDraft`, `listFunnelDrafts`, `discardFunnelDraft`, `saveFunnelEdits`.
|
|
164
183
|
|
|
165
184
|
### The loop
|
|
166
185
|
|
|
@@ -170,6 +189,7 @@ The retention counterpart to campaigns — flows with no `campaignId`.
|
|
|
170
189
|
- `{ "type": "update", "id": "<renderableId>", "renderable": { ...clone of what you read, with your changes... } }` — keep the same `id`.
|
|
171
190
|
- `{ "type": "create", "renderable": { "id": "<new-uuid>", ... }, "targetId": "<sibling id>", "position": "before" | "after" | "inside" }` — generate a fresh UUID.
|
|
172
191
|
- `{ "type": "delete", "id": "<renderableId>" }` and `{ "type": "move", "id": "...", "targetId": "...", "position": "..." }`.
|
|
192
|
+
**Need a brand-new screen?** `stageFunnelScreen` `{ "draftId": "...", "title": "...", "description"? }` stages an empty screen on the draft and returns its **permanent `screenId`** — the id is assigned at staging time, not at save, so you can immediately `stageFunnelEdit` content into it and reference it from navigation/buttons on other screens; nothing gets re-keyed at promote. On a campaign draft the screen is campaign-scoped unless you pass `"campaignScoped": false`. The screen only exists on the draft (and in draft-scoped `listFunnelScreens`/previews) until `saveFunnelEdits` promotes it, which reports it in `createdScreenIds`.
|
|
173
193
|
4. **Preview** (no CLI call): open `https://{referrer}.feastalytics.com/preview/{draftId}/{campaignId}` (drop `/{campaignId}` for a members-program draft). It renders the whole funnel as a flow diagram with the draft's edits applied. Screenshot it, then iterate — re-run `listFunnelScreens` **with the `draftId`** to read the funnel *with* the staged edits, stage more, re-preview — until it's right.
|
|
174
194
|
5. **`saveFunnelEdits`** `{ "draftId": "..." }` — **promotes to prod** (mutation, confirms first): applies the draft's edits to the live funnel and marks the draft `promoted`. To abandon instead, `discardFunnelDraft`.
|
|
175
195
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@feastalytics/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Command-line client for the Feastalytics platform — list, create, and update campaigns, automations, offers, and members-program rewards from the terminal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|