@feastalytics/cli 0.1.16 → 0.1.17
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 +2 -2
- package/dist/cli.js +172 -151
- package/feast/SKILL.md +72 -46
- package/feast/references/domains.md +9 -7
- package/feast/references/links.md +11 -11
- package/feast/references/setup.md +33 -23
- package/feast/references/workflows/ad-copy-creator.md +26 -28
- package/feast/references/workflows/ad-copy-guest.md +28 -30
- package/feast/references/workflows/ads.md +39 -27
- package/feast/references/workflows/automations.md +36 -34
- package/feast/references/workflows/campaigns.md +56 -24
- package/feast/references/workflows/creators.md +40 -36
- package/feast/references/workflows/funnels.md +15 -19
- package/feast/references/workflows/guests.md +27 -11
- package/feast/references/workflows/members-program.md +17 -13
- package/feast/references/workflows/onboarding.md +16 -17
- package/package.json +1 -1
package/feast/SKILL.md
CHANGED
|
@@ -1,105 +1,131 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: feast
|
|
3
|
-
description: Operate a Feastalytics organization
|
|
3
|
+
description: Operate a Feastalytics organization (campaigns, automations, funnels, members-program rewards, the wallet pass, creator sourcing, Meta ads, texting, onboarding, and read-only data queries) through the Feastalytics tools, either the `feast` CLI or the Feastalytics MCP server. Use this skill whenever the user wants to inspect or change Feastalytics data outside the dashboard: "list my campaigns", "create an automation for org X", "approve this creator", "publish the recruitment ad", "text this guest back", "query my guests", "update the members program", or any request to script, batch or automate Feastalytics operations. Reach for it even when the user doesn't name the CLI or the MCP server: if the task is reading or changing Feastalytics data, these are the tools.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Feast
|
|
6
|
+
# Feast
|
|
7
7
|
|
|
8
|
-
Drive the Feastalytics platform
|
|
8
|
+
Drive the Feastalytics platform with the same tool surface the in-app AI agent uses: campaigns, automations, funnels, members program, creator sourcing, Meta ads, texting, onboarding and data queries. Every tool hits the production API as the logged-in user.
|
|
9
9
|
|
|
10
|
-
The
|
|
10
|
+
The live tool list is the source of truth for *which* tools exist and *what* they accept. Read it at runtime rather than assuming, because the tool set grows as new endpoints are tagged. Your job is to pick the right tool, scope it to the right organization, and hand it valid input.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
## Two ways in
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
The same tools, with the same names and inputs, are reachable two ways. Use whichever this session has.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
| | MCP server | `feast` CLI |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| How you know you have it | Tools such as `listCampaigns` and `listOrganizations` are in your tool list | `feast tools` runs |
|
|
19
|
+
| See every tool | Your tool list | `feast tools` |
|
|
20
|
+
| One tool's input schema | Its entry in your tool list | `feast describe <tool>` |
|
|
21
|
+
| A schema that says "Named type X. Call describeSchema…" | Call `describeSchema` with `{ "names": ["X"] }` | `feast describe <tool>` prints it in full |
|
|
22
|
+
| Which organizations you can act on | `listOrganizations` | `feast whoami` |
|
|
23
|
+
| Choose the organization | The `organizationId` argument on each call | `--org <organizationId>` |
|
|
24
|
+
| Call a tool | Call it directly with its input | `feast call <tool> --org <id> --input '<json>'` (or `--input-file <path>`) |
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
Setting either one up (installing the CLI, logging in, connecting the MCP server) is in `references/setup.md`. Some environments hand you either one already connected and pinned to one organization; if a read such as `listCampaigns` works, you are set.
|
|
27
|
+
|
|
28
|
+
The rest of this skill and every workflow file names tools and their JSON input only. Translate to your transport with the table above.
|
|
29
|
+
|
|
30
|
+
## The core loop: discover, read the schema, call
|
|
23
31
|
|
|
24
|
-
|
|
32
|
+
Don't guess tool names or input shapes. Before calling an unfamiliar tool, read its input schema (and, over MCP, `describeSchema` for any named type it points to). The schema tells you the exact required fields. The CLI also validates input locally before sending, so a bad payload fails with the field named; over MCP the server validates and returns the same kind of error as a tool error.
|
|
25
33
|
|
|
26
|
-
That schema is also the boundary for what's worth asking the user about. Before sending a clarifying question, check whether the tool you're about to call has a field for the answer
|
|
34
|
+
That schema is also the boundary for what's worth asking the user about. Before sending a clarifying question, check whether the tool you're about to call has a field for the answer. A question about something the schema can't accept (a limit, a repeat rule, anything not in the schema) wastes a message and never gets used. Only ask about what the call in front of you can actually configure.
|
|
27
35
|
|
|
28
36
|
## Organizations: never let the API guess
|
|
29
37
|
|
|
30
|
-
Most tools act on one organization, and which one must be explicit
|
|
38
|
+
Most tools act on one organization, and which one must be explicit. Acting on the wrong restaurant is worse than stopping to ask.
|
|
31
39
|
|
|
32
|
-
If
|
|
40
|
+
- If the user names an organization, resolve it to its id (`listOrganizations` or `feast whoami`) and pass that id.
|
|
41
|
+
- If the user belongs to exactly one, it is used automatically.
|
|
42
|
+
- If they belong to several and you don't say which, the call is refused with a list of their organizations rather than silently picking one. Show the list and confirm which one they mean.
|
|
33
43
|
|
|
34
44
|
## Reads vs. writes
|
|
35
45
|
|
|
36
|
-
Query tools (listing, describing, reading) are safe and read-only. Mutation tools (create, update, clone, delete, apply) change production data.
|
|
46
|
+
Query tools (listing, describing, reading) are safe and read-only; over MCP they carry a read-only hint. Mutation tools (create, update, clone, delete, apply, send) change production data.
|
|
37
47
|
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
- **
|
|
48
|
+
- Always name the organization explicitly for a mutation.
|
|
49
|
+
- On the CLI, a mutation prints the organization's name to stderr before it runs (`Acting on organization <id> as <ROLE>`), so a wrong `--org` shows up as the wrong restaurant. Read that line.
|
|
50
|
+
- **Nothing asks twice.** The CLI has no confirmation prompt, and an MCP client may or may not ask the user to approve a call depending on its settings. A mutation runs the moment it is called, and nothing undoes it.
|
|
41
51
|
|
|
42
|
-
That last point matters most for the tools that reach the real world rather than just the database
|
|
52
|
+
That last point matters most for the tools that reach the real world rather than just the database:
|
|
43
53
|
|
|
44
|
-
|
|
54
|
+
- `sendText` texts a guest or creator immediately, one person per call, with no scheduling and no undo.
|
|
55
|
+
- Approving or denying a creator visit (`updateCreatorVisit`) or deciding a submission (`decideCreatorSubmission`) texts that person. `updateCreatorVisit` can preview its texts with `dryRun: true` or skip them with `sideEffects: false`; `decideCreatorSubmission` can skip its text with `skipApprovalText`.
|
|
56
|
+
- Paying a creator's bonus (`createInfluencerPayout`) charges the organization's card.
|
|
57
|
+
- `awardReward` puts a real reward in a member's wallet pass, and a retried call grants a second one.
|
|
58
|
+
- `inviteUser` sends a real email.
|
|
59
|
+
- Buying a phone number bills the account.
|
|
60
|
+
- Publishing a campaign puts it live, and pricing a recurring promotion creates real Stripe products.
|
|
61
|
+
- Activating a Meta campaign spends real ad budget.
|
|
62
|
+
- Saving automation edits changes what guests receive.
|
|
63
|
+
|
|
64
|
+
Treat those as irreversible, and get the user's intent straight *before* the call. For a text, show the user the exact message and get their go-ahead first. The one schema-level gate is `publishAds`, which requires `confirm: true` in its input; that is you confirming, not anyone asking.
|
|
65
|
+
|
|
66
|
+
Prefer reading before writing: `listCampaigns` to find the right `campaignId` before `updateCampaign`, or `listAutomationFlows` before creating a flow.
|
|
45
67
|
|
|
46
68
|
## Building good input
|
|
47
69
|
|
|
48
|
-
|
|
70
|
+
Input is a JSON object built from the tool's schema. When a tool references another entity by id (a campaign id, location id, flow id), look that id up first with the relevant list or read tool rather than inventing it.
|
|
49
71
|
|
|
50
|
-
For the domain
|
|
72
|
+
For the domain meaning of fields (how automations chain, what a funnel screen contains, how offers are structured), consult `references/domains.md` when the schema alone isn't enough.
|
|
51
73
|
|
|
52
74
|
## Workflows
|
|
53
75
|
|
|
54
|
-
Many tasks are multi-step and have a required ordering the app normally enforces. The most important rule: **automations live inside flows
|
|
76
|
+
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 and ids first, then act" shape recurs across campaigns, funnels and offers.
|
|
55
77
|
|
|
56
|
-
**Before acting on any multi-step task, read the workflow file for it.** Each one carries the required call ordering and the domain rules that make the result good rather than merely valid
|
|
78
|
+
**Before acting on any multi-step task, read the workflow file for it.** Each one carries the required call ordering and the domain rules that make the result good rather than merely valid, and neither is in the tool schemas. Read it first; don't reconstruct the sequence from tool descriptions.
|
|
57
79
|
|
|
58
80
|
| Doing this | Read |
|
|
59
81
|
|---|---|
|
|
60
82
|
| Creating, cloning or configuring a campaign; promotions | `references/workflows/campaigns.md` |
|
|
61
|
-
| Anything touching automations
|
|
83
|
+
| Anything touching automations: creating, editing, simulating, promoting a draft | `references/workflows/automations.md` |
|
|
62
84
|
| Editing funnel screens, applying a funnel template, staging a new screen | `references/workflows/funnels.md` |
|
|
63
85
|
| Writing guest-facing Meta ad copy (`adCopy`) | `references/workflows/ad-copy-guest.md` |
|
|
64
86
|
| Writing creator-recruitment ad copy (`recruitmentAdCopy`) | `references/workflows/ad-copy-creator.md` |
|
|
65
87
|
| Publishing, pausing, budgeting or diagnosing Meta ads | `references/workflows/ads.md` |
|
|
66
|
-
| Creator sourcing
|
|
67
|
-
| Members-program rewards; reading or saving the wallet pass configuration | `references/workflows/members-program.md` |
|
|
88
|
+
| Creator sourcing: approving applicants, reviewing content, creatives, payouts, reimbursements, texting a creator | `references/workflows/creators.md` |
|
|
89
|
+
| Members-program rewards, granting a reward to one member; reading or saving the wallet pass configuration | `references/workflows/members-program.md` |
|
|
68
90
|
| Working the onboarding taskboard; brand identity; phone, media, invites, billing | `references/workflows/onboarding.md` |
|
|
69
|
-
| Searching guests
|
|
91
|
+
| Searching guests and members, texting a guest back, querying anything through the data catalog | `references/workflows/guests.md` |
|
|
70
92
|
|
|
71
|
-
Every row names one file, and one file is the whole answer for that row
|
|
93
|
+
Every row names one file, and one file is the whole answer for that row. Pick the row that matches what you're doing and read only it. The two ad-copy rows are mutually exclusive: you are writing to guests or to creators, never both in one piece of copy.
|
|
72
94
|
|
|
73
|
-
Read more than one file when a task genuinely spans steps
|
|
95
|
+
Read more than one file when a task genuinely spans steps. A new campaign usually means `campaigns.md` plus `automations.md` and `funnels.md`. Read each one as you reach that step rather than gathering them up front: a file stays in context for the rest of the session, so one you open speculatively is re-read on every later turn.
|
|
74
96
|
|
|
75
|
-
When the ask is a question rather than a change
|
|
97
|
+
When the ask is a question rather than a change (where something lives, what a field means, which link to send), read the single file the table names and answer from it.
|
|
76
98
|
|
|
77
|
-
Some things
|
|
99
|
+
Some things have no tool: firing an automation at a live member, pass image generation, ad-copy generation (write it yourself), and publishing creator content as partnership ads. The workflow files say which. Don't fabricate a call for a tool that isn't in the tool list; tell the user that part has to be done in the dashboard.
|
|
78
100
|
|
|
79
101
|
## Link to what you touched
|
|
80
102
|
|
|
81
|
-
Work you do through
|
|
103
|
+
Work you do through these tools 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. 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.
|
|
82
104
|
|
|
83
|
-
**You don't know these URLs
|
|
105
|
+
**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.
|
|
84
106
|
|
|
85
|
-
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
|
|
107
|
+
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 suppress analytics versus merely tagging a visit as a preview.
|
|
86
108
|
|
|
87
109
|
## Worked example
|
|
88
110
|
|
|
89
111
|
User: "add a Free Dessert reward members can redeem for 100 points in my Plum Vietnamese org."
|
|
90
112
|
|
|
113
|
+
1. Find the organization's id with `listOrganizations` (CLI: `feast whoami`), unless you were given it.
|
|
114
|
+
2. Read the `createMembersProgramReward` schema to learn the input shape (`type: "item"` vs `type: "name"`).
|
|
115
|
+
3. Call `listMembersProgramRewards` to avoid duplicating an existing reward or catalog item.
|
|
116
|
+
4. Call `createMembersProgramReward` with `{ "type": "name", "name": "Free Dessert", "pointsCost": 100 }`.
|
|
117
|
+
|
|
118
|
+
On the CLI, step 4 is:
|
|
119
|
+
|
|
91
120
|
```bash
|
|
92
|
-
feast
|
|
93
|
-
feast describe createMembersProgramReward # learn the input shape (type: item vs name)
|
|
94
|
-
feast call listMembersProgramRewards --org <orgId> # avoid duplicating an existing reward or catalog item
|
|
95
|
-
feast call createMembersProgramReward --org <orgId> --input '{"type":"name","name":"Free Dessert","pointsCost":100}'
|
|
121
|
+
feast call createMembersProgramReward --org <organizationId> --input '{"type":"name","name":"Free Dessert","pointsCost":100}'
|
|
96
122
|
```
|
|
97
123
|
|
|
98
|
-
The pattern generalizes: identify the
|
|
124
|
+
The pattern generalizes: identify the organization, learn the tool, resolve any referenced ids, then act.
|
|
99
125
|
|
|
100
126
|
## When something fails
|
|
101
127
|
|
|
102
|
-
-
|
|
103
|
-
- "You belong to multiple organizations"
|
|
104
|
-
-
|
|
105
|
-
- A tool you expected isn't
|
|
128
|
+
- Not logged in, session expired, `feast` isn't on PATH, or the MCP server asks you to authenticate: `references/setup.md`.
|
|
129
|
+
- "You belong to multiple organizations": pick one and pass it explicitly (see Organizations above).
|
|
130
|
+
- The input doesn't match the schema: re-read the tool's schema (and any named type) and fix the named fields.
|
|
131
|
+
- A tool you expected isn't in the tool list: don't fabricate a call; tell the user that part has to be done in the dashboard.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Feastalytics domain model
|
|
2
2
|
|
|
3
|
-
Background for constructing tool input correctly. This is the conceptual map; the authoritative field list for any tool always comes from
|
|
3
|
+
Background for constructing tool input correctly. This is the conceptual map; the authoritative field list for any tool always comes from its input schema.
|
|
4
4
|
|
|
5
5
|
## Organizations
|
|
6
6
|
|
|
7
|
-
The top-level tenant. Nearly every tool is scoped to one organization
|
|
7
|
+
The top-level tenant. Nearly every tool is scoped to one organization (see Organizations in `SKILL.md`). An org has one or more POS locations (Toast/Square/Clover); many tools that operate on menus or offers need a `locationId`, which you get from `getOrganization` (it returns the org's `locations`), not the organization id.
|
|
8
8
|
|
|
9
9
|
## Campaigns (acquisition)
|
|
10
10
|
|
|
@@ -13,7 +13,9 @@ A campaign is an acquisition effort. It bundles:
|
|
|
13
13
|
- **automations** (see below) scoped to that campaign,
|
|
14
14
|
- **promotions/offers** attached to it.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
`listCampaigns` resolves a `campaignId`: use each summary's `id` (a UUID), not the nested Meta campaign id. Summaries also carry the name, `shorthand` (used in reservation links), publish state and referrers; `getCampaign` has the full configuration.
|
|
17
|
+
|
|
18
|
+
Typical flow: `createCampaign`, then always `populateCampaign` (a new campaign stays hidden from the dashboard until it is populated), then choose a funnel template. `cloneCampaign` duplicates an existing one (funnel, automations and offers); it needs the source campaign id and a `referrer` (a subdomain from the org's `subdomains2`).
|
|
17
19
|
|
|
18
20
|
## Automations and flows
|
|
19
21
|
|
|
@@ -29,16 +31,16 @@ Promotions live on the campaign record (`getCampaign` / `updateCampaign`), and r
|
|
|
29
31
|
|
|
30
32
|
## Members program (retention)
|
|
31
33
|
|
|
32
|
-
The retention counterpart to campaigns: rewards and pass configuration for returning guests. Members-program automations are the flows with no `campaignId` (`scope: "membersProgram"` above). Rewards are fully manageable (`listMembersProgramRewards` / `createMembersProgramReward` / `updateMembersProgramReward` / `deleteMembersProgramReward`), and the wallet pass is a read-modify-write document (`getPassConfiguration` / `updatePassConfiguration`)
|
|
34
|
+
The retention counterpart to campaigns: rewards and pass configuration for returning guests. Members-program automations are the flows with no `campaignId` (`scope: "membersProgram"` above). Rewards are fully manageable (`listMembersProgramRewards` / `createMembersProgramReward` / `updateMembersProgramReward` / `deleteMembersProgramReward`), and the wallet pass is a read-modify-write document (`getPassConfiguration` / `updatePassConfiguration`). See `workflows/members-program.md`.
|
|
33
35
|
|
|
34
36
|
## Creator sourcing
|
|
35
37
|
|
|
36
|
-
Restaurants recruit local content creators to visit and post. One config per location (`getInfluencerBoardConfig`), an approval queue of applications, content review, and bonus payouts
|
|
38
|
+
Restaurants recruit local content creators to visit and post. One config per location (`getInfluencerBoardConfig`), an approval queue of applications, content review, and bonus payouts. See `workflows/creators.md`. Recruitment *ads* publish through the Meta ads surface (`workflows/ads.md`).
|
|
37
39
|
|
|
38
40
|
## Meta ads
|
|
39
41
|
|
|
40
|
-
A template-driven publish pipeline: `listAdTemplates` → `planAds` → `publishAds` → `getJob` → `setAdCampaignStatus`, plus `ads_*` tools for reading and steering what's already on the ad account
|
|
42
|
+
A template-driven publish pipeline: `listAdTemplates` → `planAds` → `publishAds` → `getJob` → `setAdCampaignStatus`, plus `ads_*` tools for reading and steering what's already on the ad account. See `workflows/ads.md`.
|
|
41
43
|
|
|
42
44
|
## The data catalog
|
|
43
45
|
|
|
44
|
-
`describeData` / `queryData` expose a read-only, org-scoped query surface over the data model
|
|
46
|
+
`describeData` / `queryData` expose a read-only, org-scoped query surface over the data model: guests, orders, menu items, texts, creator visits, payouts. When no purpose-built tool answers a read question, the catalog usually does; `describeData` with no arguments is the index.
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# Linking to your work
|
|
2
2
|
|
|
3
|
-
Most of what you create or change through
|
|
3
|
+
Most of what you create or change through these tools 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
4
|
|
|
5
|
-
So offer links freely: after a turn where you created, changed, or published something, close with a short markdown list
|
|
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
6
|
|
|
7
|
-
You can build almost every URL below from ids you already have. `<organizationId>` is the
|
|
7
|
+
You can build almost every URL below from ids you already have. `<organizationId>` is the organization id you pass on every call. Campaign, flow and draft ids come back from the tool call you just made. Only `<subdomain>` needs a lookup.
|
|
8
8
|
|
|
9
|
-
Angle brackets below mark a value **you** substitute. A finished link contains no brackets, no braces and no backticks
|
|
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
10
|
|
|
11
11
|
## Dashboard
|
|
12
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
|
|
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
14
|
|
|
15
15
|
```
|
|
16
16
|
/ home
|
|
@@ -31,22 +31,22 @@ Members-program panels: `overview`, `funnel`, `automations`, `pass-builder`, `re
|
|
|
31
31
|
|
|
32
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
33
|
|
|
34
|
-
Funnels are always edited inside a panel, never on a page of their own
|
|
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
35
|
|
|
36
36
|
### Onboarding task pages
|
|
37
37
|
|
|
38
|
-
Task entries from `getTaskboard` come with a ready-made `completionUrl
|
|
38
|
+
Task entries from `getTaskboard` come with a ready-made `completionUrl`. Always prefer pasting that over constructing a URL. The shape behind it: `https://feastalytics.com/tasks/<organizationId>` is the org's standalone task list, and `https://feastalytics.com/tasks/<organizationId>/<taskId>` opens one task's completion UI directly (chrome-less; works in the dashboard's agent preview panel and as a normal browser link). These are the links to hand over when a task needs the human: OAuth connections, phone purchase, device setup.
|
|
39
39
|
|
|
40
40
|
### Automation previews
|
|
41
41
|
|
|
42
|
-
These two hang off the **root**, not off `/<organizationId>/app
|
|
42
|
+
These two hang off the **root**, not off `/<organizationId>/app`. The organization id is the first path segment:
|
|
43
43
|
|
|
44
44
|
```
|
|
45
45
|
https://feastalytics.com/automation-preview/<organizationId>/<flowId>
|
|
46
46
|
https://feastalytics.com/automation-preview/<organizationId>/<flowId>?draftId=<draftId>
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
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
|
|
49
|
+
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.
|
|
50
50
|
|
|
51
51
|
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.
|
|
52
52
|
|
|
@@ -73,10 +73,10 @@ The `/preview/<draftId>` route is the payoff of the draft → preview → promot
|
|
|
73
73
|
|
|
74
74
|
## Worked example
|
|
75
75
|
|
|
76
|
-
After creating a campaign and applying a funnel template. Every id below is substituted
|
|
76
|
+
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:
|
|
77
77
|
|
|
78
78
|
```markdown
|
|
79
|
-
Done
|
|
79
|
+
Done. "Fall Prix Fixe" is live as a draft.
|
|
80
80
|
|
|
81
81
|
- [Open the campaign](https://feastalytics.com/3e8cb27c-6e54-444b-859f-66dbae0e711b/app/campaigns/e8ccc852-6555-4a9a-b48b-127d687bb34a)
|
|
82
82
|
- [Edit the funnel](https://feastalytics.com/3e8cb27c-6e54-444b-859f-66dbae0e711b/app/campaigns/e8ccc852-6555-4a9a-b48b-127d687bb34a?panel=funnel-v2)
|
|
@@ -1,31 +1,45 @@
|
|
|
1
1
|
# Setup, auth, and organizations
|
|
2
2
|
|
|
3
|
-
One-time and troubleshooting material:
|
|
3
|
+
One-time and troubleshooting material: connecting the Feastalytics MCP server or installing and logging in to the `feast` CLI, keeping the CLI and this skill current, and working out which organization to act on. The day-to-day loop lives in `SKILL.md`; you only need this file when something isn't working yet.
|
|
4
4
|
|
|
5
|
-
Some environments hand you
|
|
5
|
+
Some environments hand you the tools already connected and authenticated, pinned to a single organization. Nothing in this file applies there: if a read such as `listCampaigns` works and your calls are going to the right restaurant, you are already set up.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## The MCP server
|
|
8
|
+
|
|
9
|
+
The hosted server is at `https://mcp.feast-api.com/mcp` (streamable HTTP, OAuth). The user adds it once in their MCP client, for example:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
claude mcp add --transport http feast https://mcp.feast-api.com/mcp # Claude Code
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
In other clients (Claude Desktop, claude.ai, Cursor) it is a custom connector with that URL. The first call opens a browser to the Feastalytics login, then a consent page naming the client; after that the client refreshes the session itself. A session lasts 30 days from that login, then the user logs in again. If tool calls start failing with an authentication error, ask the user to reconnect the server in their client.
|
|
16
|
+
|
|
17
|
+
You can't add the server for the user from inside a conversation. Tell them the URL and where to add it.
|
|
18
|
+
|
|
19
|
+
## The `feast` CLI
|
|
20
|
+
|
|
21
|
+
### Installing
|
|
8
22
|
|
|
9
23
|
The `feast` CLI must be installed and on PATH:
|
|
10
24
|
|
|
11
25
|
```bash
|
|
12
|
-
npm install -g @feastalytics/cli # or run ad
|
|
26
|
+
npm install -g @feastalytics/cli # or run ad hoc with: npx @feastalytics/cli <command>
|
|
13
27
|
```
|
|
14
28
|
|
|
15
|
-
If the global install fails on permissions, don't retry with `sudo
|
|
29
|
+
If the global install fails on permissions, don't retry with `sudo`. Tell the user and fall back to `npx @feastalytics/cli@latest`.
|
|
16
30
|
|
|
17
|
-
|
|
31
|
+
### Authenticating
|
|
18
32
|
|
|
19
|
-
Authenticate once
|
|
33
|
+
Authenticate once. Tokens are cached in `~/.config/feast-cli/credentials.json` and refreshed automatically:
|
|
20
34
|
|
|
21
35
|
```bash
|
|
22
36
|
feast login # opens a browser to authorize (default)
|
|
23
|
-
feast login --password [username] # headless
|
|
37
|
+
feast login --password [username] # headless or CI: username and password prompt, no browser
|
|
24
38
|
```
|
|
25
39
|
|
|
26
|
-
If a command reports you're not logged in or the session expired,
|
|
40
|
+
If a command reports you're not logged in or the session expired, run `feast login` again.
|
|
27
41
|
|
|
28
|
-
|
|
42
|
+
### Staying current
|
|
29
43
|
|
|
30
44
|
Neither the CLI nor this skill updates itself. When a command prints an update notice on stderr:
|
|
31
45
|
|
|
@@ -36,17 +50,17 @@ Update available: feast 0.1.1 → 0.2.0
|
|
|
36
50
|
update both, then tell the user in one line that you did:
|
|
37
51
|
|
|
38
52
|
```bash
|
|
39
|
-
npm install -g @feastalytics/cli@latest # only if `feast` is
|
|
53
|
+
npm install -g @feastalytics/cli@latest # only if `feast` is on PATH from a global install
|
|
40
54
|
npx skills add feastalytics/cli -g -a '*' -y # refresh this skill from the repo
|
|
41
55
|
```
|
|
42
56
|
|
|
43
|
-
If you've been invoking the CLI through `npx` rather than a global install, skip the `npm install -g` and use `npx @feastalytics/cli@latest <command>` for the rest of the session instead
|
|
57
|
+
If you've been invoking the CLI through `npx` rather than a global install, skip the `npm install -g` and use `npx @feastalytics/cli@latest <command>` for the rest of the session instead (`npx` reuses a cached copy otherwise).
|
|
44
58
|
|
|
45
|
-
Update the skill whenever you update the CLI: the two ship from the same repo but on different triggers, so a new CLI version usually means this skill's guidance has moved too.
|
|
59
|
+
Update the skill whenever you update the CLI: the two ship from the same repo but on different triggers, so a new CLI version usually means this skill's guidance has moved too. The MCP server always serves the current tools, so over MCP only the skill needs refreshing.
|
|
46
60
|
|
|
47
61
|
## Playbook skills
|
|
48
62
|
|
|
49
|
-
Feastalytics publishes further skills that build on this one (campaign diagnosis and other playbooks). They come from the API, not from GitHub:
|
|
63
|
+
Feastalytics publishes further skills that build on this one (campaign diagnosis and other playbooks). They come from the API, not from GitHub, and installing them takes the CLI:
|
|
50
64
|
|
|
51
65
|
```bash
|
|
52
66
|
feast skill list # what is published for your organization
|
|
@@ -59,12 +73,8 @@ Install what `feast skill list` offers when the user asks for a playbook this sk
|
|
|
59
73
|
|
|
60
74
|
Most tools act on one organization. A user often belongs to several, so which one you target matters and must be explicit.
|
|
61
75
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
- If the user names an org, resolve it to its id with `feast whoami` and pass that id.
|
|
69
|
-
- If the user belongs to exactly one org, the CLI uses it automatically — no flag needed.
|
|
70
|
-
- If they belong to more than one and you omit `--org`, the CLI refuses and lists the orgs rather than silently picking one. That's intentional: acting on the wrong org is worse than stopping to ask. When this happens, surface the list to the user and confirm which one they mean.
|
|
76
|
+
- See every organization the user can act on, with names and their role: `listOrganizations` over MCP, `feast whoami` on the CLI.
|
|
77
|
+
- Pass the target as the `organizationId` argument (MCP) or `--org <organizationId>` (CLI).
|
|
78
|
+
- If the user names an organization, resolve it to its id from that list and pass the id.
|
|
79
|
+
- If the user belongs to exactly one organization, it is used automatically.
|
|
80
|
+
- If they belong to more than one and you don't pass one, the call is refused with the list of their organizations rather than silently picking one. That's intentional: acting on the wrong organization is worse than stopping to ask. Show the user the list and confirm which one they mean.
|
|
@@ -1,45 +1,43 @@
|
|
|
1
1
|
# Creator-recruitment ad copy (`recruitmentAdCopy`)
|
|
2
2
|
|
|
3
|
-
> Part of the Feastalytics CLI workflows. Confirm a tool exists with `feast tools` before relying on it, and get its exact fields from `feast describe <tool>` — this file gives the *meaning* and *ordering* the schema can't.
|
|
4
|
-
|
|
5
3
|
This is the creator-facing half of Meta ad copy: `recruitmentAdCopy`, which sells a paid collaboration to a content creator shopping for brand deals.
|
|
6
4
|
|
|
7
|
-
**Writing to guests instead? Read `ad-copy-guest.md` and not this file.** `adCopy` sells the offer and the food to a hungry local scrolling past
|
|
5
|
+
**Writing to guests instead? Read `ad-copy-guest.md` and not this file.** `adCopy` sells the offer and the food to a hungry local scrolling past. That is a different audience and a completely different pitch. Conflating the two is the failure mode in this area, and it has happened repeatedly. A creator is not a customer; the food is their perk, not the pitch. If you catch yourself writing "claim your voucher" here, stop and start over.
|
|
8
6
|
|
|
9
|
-
For the creator program itself
|
|
7
|
+
For the creator program itself (applicants, visits, briefs, the decision loop), read `creators.md`. Publishing the finished ad is a separate job with its own loop: read `ads.md`.
|
|
10
8
|
|
|
11
|
-
**You write the copy yourself.** The dashboard has a "generate copy" button behind an LLM call; there is no
|
|
9
|
+
**You write the copy yourself.** The dashboard has a "generate copy" button behind an LLM call; there is no tool for it and you shouldn't want one, because it would be you calling an endpoint in order to call a model. The copy lands on a plain field of the campaign record, so saving it is trivial and covered at the bottom of this file. Everything between here and there is the part that's actually hard.
|
|
12
10
|
|
|
13
11
|
## Variations: write a set, not a single
|
|
14
12
|
|
|
15
|
-
Meta's Advantage+ creative optimization tests combinations of headlines and primary texts against each other, so you're writing a *set
|
|
13
|
+
Meta's Advantage+ creative optimization tests combinations of headlines and primary texts against each other, so you're writing a *set*: several headlines and several primary texts that genuinely differ.
|
|
16
14
|
|
|
17
15
|
**Genuinely** is the load-bearing word. There is no required count. Three sharp variations that each take a real angle beat five where two are padding, and a set of near-identical rewrites teaches the optimizer nothing. Write as many as the collab actually supports. Judge it, and stop when the next variation would be filler.
|
|
18
16
|
|
|
19
|
-
The other reason to write more than one is that the restaurant may want to pick, and people form opinions by seeing alternatives rather than by being handed a single answer. So offering the set is usually the right move
|
|
17
|
+
The other reason to write more than one is that the restaurant may want to pick, and people form opinions by seeing alternatives rather than by being handed a single answer. So offering the set is usually the right move, but you have taste, and you should use it. Recommend the one you'd launch and say why. Cut the weak ones before anyone sees them rather than padding them in to look thorough. Three you'd defend beats five you wouldn't.
|
|
20
18
|
|
|
21
19
|
## The audience
|
|
22
20
|
|
|
23
|
-
**Local food and lifestyle content creators** on Instagram and TikTok
|
|
21
|
+
**Local food and lifestyle content creators** on Instagram and TikTok: someone scrolling for brand collabs, not a hungry person hunting a deal. The ad is decoupled from any consumer campaign the restaurant is running, *even if one is running right now*. Your job is to get the right creator to tap "Learn More" on a landing page that explains the collab in full, not to close the deal inside the ad.
|
|
24
22
|
|
|
25
|
-
## Absolute rules
|
|
23
|
+
## Absolute rules: this is exactly where past generations went wrong
|
|
26
24
|
|
|
27
25
|
- **Never mention an offer, deal, voucher, promotion, discount, "claiming" anything, or pre-paying.** This is a collaboration, not a customer offer.
|
|
28
26
|
- **Don't pitch the food the way you'd pitch it to a diner.** The food is the perk; the collab is the pitch.
|
|
29
|
-
- **No customer-facing language
|
|
30
|
-
- **Don't reuse the campaign's guest-facing framing
|
|
27
|
+
- **No customer-facing language**: "claim your voucher", "come hungry", "limited time offer", "this week only", "tap below to save".
|
|
28
|
+
- **Don't reuse the campaign's guest-facing framing**: banner copy, promotions, offer headlines. None of it belongs here, however good it is.
|
|
31
29
|
- **If there's a cash bonus, never imply it's automatic or guaranteed.** It is earned only if the restaurant selects the creator's reel to run as a paid ad. Phrasings like "earn a $100 bonus", "get a $100 bonus when you post", or "$100 bonus if you nail the brief" read as guaranteed-on-completion, and they have caused real creators to demand a bonus they hadn't earned. Always frame it conditionally: "a chance to earn", "up to", "if your reel gets picked to run as an ad".
|
|
32
30
|
|
|
33
31
|
## What to pitch
|
|
34
32
|
|
|
35
33
|
- The restaurant is booking local food/lifestyle creators to come in, eat on the house, and post a short reel.
|
|
36
|
-
- **The creator gets:** a dining credit (order whatever they want), a creative brief with style direction but no script
|
|
37
|
-
- **The creator gives:** one 30
|
|
34
|
+
- **The creator gets:** a dining credit (order whatever they want), a creative brief with style direction but no script (they stay authentic), and, when acquisition is enabled, their reel boosted as a paid partnership ad alongside the restaurant's Instagram, which is free promotion to thousands of local foodies plus followers and engagement on their own page. Where a bonus exists, add it conditionally.
|
|
35
|
+
- **The creator gives:** one 30 to 60 second vertical reel (Instagram Reel / TikTok), filmed during the visit, submitted within 72 hours.
|
|
38
36
|
- **Eligibility:** an active food/lifestyle creator with a minimum local-area follower count on Instagram or TikTok.
|
|
39
37
|
|
|
40
|
-
## Angles
|
|
38
|
+
## Angles: rotate across the set
|
|
41
39
|
|
|
42
|
-
With a bonus: **get paid** ("Get paid to eat at X") → **free food + free promotion**, both sides of the exchange → **local creator call-out** ("Local foodies on IG
|
|
40
|
+
With a bonus: **get paid** ("Get paid to eat at X") → **free food + free promotion**, both sides of the exchange → **local creator call-out** ("Local foodies on IG, we want you") → **grow your page**, the boost and the new followers → **straightforward collab pitch**, no fluff: free meal + paid post + bonus.
|
|
43
41
|
|
|
44
42
|
Without a bonus, swap the first for **free food collab** ("Eat on us at X"), and grow-your-page for **brand partnership** (a collaboration, not a giveaway) or **behind-the-scenes** (be part of the restaurant's story).
|
|
45
43
|
|
|
@@ -49,7 +47,7 @@ Take as many of these as the collab genuinely supports rather than filling a quo
|
|
|
49
47
|
|
|
50
48
|
Talk like a brand DM'ing a creator about a collab, not like a restaurant running an ad. Confident, peer-to-peer, slightly insider: "We're partnering with…", "We're booking creators for…", "Looking for local foodies who…".
|
|
51
49
|
|
|
52
|
-
Specifics over fluff
|
|
50
|
+
Specifics over fluff: name the dollar amounts, the deliverable (one reel, 30 to 60s), the eligibility. Emoji fine in moderation (📸 🎥 🍴), don't spam. **Separate every sentence with a blank line (two newlines)**. Each sentence has to stand alone visually, because a paragraph is a wall and a wall gets skipped.
|
|
53
51
|
|
|
54
52
|
**GOOD primary text:**
|
|
55
53
|
|
|
@@ -58,36 +56,36 @@ Plum Vietnamese is booking local food creators this month. 📸
|
|
|
58
56
|
|
|
59
57
|
You get a $30 tab on us, a creative brief, and a chance to earn a $100 bonus if your reel gets run as a paid ad.
|
|
60
58
|
|
|
61
|
-
We'll also boost it as a partnership ad
|
|
59
|
+
We'll also boost it as a partnership ad: free promo to thousands of local foodies. 1,000+ local IG/TikTok followers to apply.
|
|
62
60
|
```
|
|
63
61
|
|
|
64
|
-
**GOOD headlines:** `Get paid to post about Plum` · `Local creators
|
|
62
|
+
**GOOD headlines:** `Get paid to post about Plum` · `Local creators: eat free, post a reel` · `Foodies w/ 1,000+ followers, read this 👀`
|
|
65
63
|
|
|
66
|
-
**BAD
|
|
64
|
+
**BAD (do not generate this):**
|
|
67
65
|
|
|
68
66
|
```
|
|
69
|
-
Free meal at Plum Vietnamese this week! Claim your voucher and come hungry
|
|
67
|
+
Free meal at Plum Vietnamese this week! Claim your voucher and come hungry, you won't want to miss this deal. 🍴
|
|
70
68
|
```
|
|
71
69
|
|
|
72
|
-
That's a customer offer wearing a creator ad's clothes. Every one of "free meal", "claim your voucher", "come hungry" and "this deal" is independently disqualifying. Note what the good version does instead: it names the restaurant as the one doing the booking, states the exchange in plain numbers, and gates on follower count
|
|
70
|
+
That's a customer offer wearing a creator ad's clothes. Every one of "free meal", "claim your voucher", "come hungry" and "this deal" is independently disqualifying. Note what the good version does instead: it names the restaurant as the one doing the booking, states the exchange in plain numbers, and gates on follower count, so the wrong reader self-selects out in the first line.
|
|
73
71
|
|
|
74
|
-
## The terms are baked into the copy
|
|
72
|
+
## The terms are baked into the copy: record them
|
|
75
73
|
|
|
76
|
-
`foodCreditCents`, `creatorPayoutCents` and `minFollowerCount` on `recruitmentAdCopy` record the terms your copy actually stated. The dashboard compares them against the live creator board config and flags the copy as drifted when they diverge
|
|
74
|
+
`foodCreditCents`, `creatorPayoutCents` and `minFollowerCount` on `recruitmentAdCopy` record the terms your copy actually stated. The dashboard compares them against the live creator board config and flags the copy as drifted when they diverge, so if you write "$30 tab" and leave them unset, nobody finds out when the credit later changes to $50 and the ad starts lying.
|
|
77
75
|
|
|
78
|
-
**Read the creator board config with `getInfluencerBoardConfig` first
|
|
76
|
+
**Read the creator board config with `getInfluencerBoardConfig` first.** The dining credit, the bonus and the follower minimum live there and nowhere else. Write those exact numbers into the copy, and mirror them into these fields (in **cents** for the two money fields). Don't guess them, and don't ask the user for numbers the config already has.
|
|
79
77
|
|
|
80
|
-
The creator landing page is `/creator-landing` on the org's subdomain with `orgId`, `locId`, `campaignId` and UTM params
|
|
78
|
+
The creator landing page is `/creator-landing` on the org's subdomain with `orgId`, `locId`, `campaignId` and UTM params, fiddly enough that you should reuse the existing `recruitmentAdCopy.landingPageUrl` when the campaign already has copy, rather than reconstructing it.
|
|
81
79
|
|
|
82
80
|
## Saving it
|
|
83
81
|
|
|
84
|
-
One `updateCampaign` call writes `recruitmentAdCopy`.
|
|
82
|
+
One `updateCampaign` call writes `recruitmentAdCopy`. Read `updateCampaign`'s input schema for the fields: alongside the headlines and primary texts it wants the landing page URL, the creative mix, a timestamp, and optional indices for the variation you're recommending.
|
|
85
83
|
|
|
86
|
-
**The one thing the schema won't tell you: `update.
|
|
84
|
+
**The one thing the schema won't tell you: `update.recruitmentAdCopy` replaces the whole object rather than merging into it.** Read the campaign with `getCampaign` first and send back everything you want kept, not just what changed.
|
|
87
85
|
|
|
88
86
|
## Publishing
|
|
89
87
|
|
|
90
|
-
**
|
|
88
|
+
**Drivable through the tools; read `ads.md`.** The loop is `listAdTemplates` → gather variables → `planAds` → `publishAds` (with its effects) → `getJob` → `setAdCampaignStatus`, and that file carries the ordering, the idempotency-key discipline, and the effect declarations that make the publish self-bookkeeping.
|
|
91
89
|
|
|
92
90
|
---
|
|
93
91
|
|