@flowapt/flowiq-cli 0.12.1 → 0.12.2

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 CHANGED
@@ -1292,9 +1292,9 @@ flowiq hours package clear <org_id> # back to "no package on file"
1292
1292
 
1293
1293
  ### Client deck — `flowiq report deck status|build|narrate|generate|inputs|approve|unapprove|send|print|pull` (v0.6.5)
1294
1294
 
1295
- The monthly client deck (`/reporting/deck` in the app; 10 slides, 11 for a store
1296
- org — slide 8 is store health: cart abandonment, returning buyers and opt-in
1297
- growth, online store only), driven from the
1295
+ The monthly client deck (`/reporting/deck` in the app; a cover and five slides
1296
+ since 5 Oct 2026: month at a glance, where WhatsApp made money, broadcasts and
1297
+ always-on, your customers, what we did and what's next), driven from the
1298
1298
  terminal. Everything runs server-side through the `flowiq-reporting-deck` edge
1299
1299
  function: the metrics are built from the FROZEN month snapshot, the copy is
1300
1300
  written by the model under the report rules and a numeric guard (every figure
@@ -1308,6 +1308,7 @@ flowiq report deck generate <org_id> 2026-08 # build the metrics (
1308
1308
  flowiq report deck generate <org_id> 2026-08 --refresh --force # rebuild everything
1309
1309
  flowiq report deck build <org_id> 2026-08 --refresh # metrics only; status then says "Copy is older than the numbers" until you narrate
1310
1310
  flowiq report deck narrate <org_id> 2026-08 # copy only (hand edits are kept)
1311
+ flowiq report deck narrate <org_id> 2026-08 --only changes,action_points # rewrite just these copy fields, keep the rest as written
1311
1312
  flowiq report deck inputs <org_id> 2026-08 --file inputs.json # the super-admin input form
1312
1313
  flowiq report deck approve <org_id> 2026-08 # refused until every required input is present
1313
1314
  flowiq report deck send <org_id> 2026-08 --test-to me@flowapt.com # one test copy, status untouched
@@ -1326,9 +1327,9 @@ flowiq report deck pull <org_id> 2026-08 # → ./.flowiq/repor
1326
1327
  Free text (milestone, the four plan fields) is edited in the app; it is stored as overrides that survive `narrate`.
1327
1328
  - `send --client` needs an approved deck AND `report.email.recipients` in the org's reporting config (Control center → Report delivery); it marks the deck `sent`. `--test-to` never changes status. Fees on the slides are the org's actual Meta billing when the token can read it, otherwise the rate-card estimate, and the footnote says which.
1328
1329
  - Non-store orgs need `config.deck.outcome` (`source: handover | ticket_status | tag | keyword`, labels) — the revenue slides become outcome slides. Every verb except `status` and `pull` is audited.
1329
- - A month with a Customer Insights report gets the "What your customers asked" slide after slide 7 (29 Sep 2026): `build` takes the one `org_insights` export-insights row whose window covers the most of the month (top questions, trending topics, complaints or product mentions, sentiment, each as "N customers"), stores it under `deck.insights`, and the copy carries `insights_title` + `insights_read`. `status --json` shows the window under `insights` (null = no report, no slide). The timing slide's next-step card is now written by the narrator (`next_step_title` / `next_step_body`) against next month's South African retail calendar (public holidays, Mother's / Father's Day, Black Friday, payday, month-end) and edited in place like every other block.
1330
+ - A month with a Customer Insights report gets its top questions and topics on the "Your customers" slide (a slide of its own from 29 Sep to 5 Oct 2026): `build` takes the one `org_insights` export-insights row whose window covers the most of the month (top questions, trending topics, complaints or product mentions, sentiment, each as "N customers"), stores it under `deck.insights`, and the copy carries `insights_title` + `insights_read`. `status --json` shows the window under `insights` (null = no report, no slide). The timing slide's next-step card is now written by the narrator (`next_step_title` / `next_step_body`) against next month's South African retail calendar (public holidays, Mother's / Father's Day, Black Friday, payday, month-end) and edited in place like every other block.
1330
1331
  - Cart funnel and buckets (29 Sep 2026): "messaged" counts reminders WhatsApp reports delivered (the old send-request count rides along as `messaged_requested`); a Woo cart reminder now matches its order, so Woo recoveries are no longer zero; our own cart link is a recovery, another tool's abandoned-cart email is not; on Woo an agent-built order is a conversation, not a campaign; chat-to-order counts buyers who chatted, any bucket. `status` refuses approval when the agent replied to nobody in the month (no live channel).
1331
- - Store orgs get slide 8, "Store health": cart abandonment (online checkouts started, bought, abandoned), first-time vs returning buyers and opt-in growth, each with a twelve-month strip and measured on the online store checkout only (till and other channels are named in the footer). `status --json` carries the headline figures under `store`; `config.deck.channel_labels {"<source_name>": "Label"}` renames a sales channel on the slide.
1332
+ - Store orgs: the deck still builds store health (its own slide until 5 Oct 2026; the five-slide deck does not show it as a slide): cart abandonment (online checkouts started, bought, abandoned), first-time vs returning buyers and opt-in growth, each with a twelve-month strip and measured on the online store checkout only (till and other channels are named in the footer). `status --json` carries the headline figures under `store`; `config.deck.channel_labels {"<source_name>": "Label"}` renames a sales channel on the slide.
1332
1333
 
1333
1334
  ### Insights — `flowiq insights status|enable|disable|run` (v0.7.0)
1334
1335
 
@@ -1789,7 +1790,7 @@ pairs with reasoning models like `gpt-5.6-luna`; `max` is GPT-6 and Claude only)
1789
1790
  the tool-flag columns (`woo_order_build`, `woo_tip_field`, `woo_order_note_field`,
1790
1791
  `view_cart_tool`, `restock_tool`, `block_tool_status`, `postal_code_tool_status`,
1791
1792
  `shopify_products_web_chat`, `ticket_tool_status`, `product_lookup`,
1792
- `collapse_product_variants`, `email_request_tool`, `silent_option`),
1793
+ `collapse_product_variants`, `email_request_tool`, `silent_option`, `reaction_tool`),
1793
1794
  `discount.enabled`, and the `flowiq test` contact
1794
1795
  (`settings.test_contact_number` / `settings.test_contact_name`). Since 0.9.8 also:
1795
1796
 
@@ -1835,6 +1836,13 @@ no staff WhatsApp — built for businesses that want requests in an inbox rather
1835
1836
  human escalation (GIB Financial Services). The recipient list is set in the Tool
1836
1837
  Library (Agents → Tool Library → Email Request to Team) or with `--email-request-to` (0.9.8).
1837
1838
 
1839
+ **`--tool reaction_tool=true` (added 6 Oct 2026).** Turns on the `react_to_message` tool:
1840
+ the agent reacts to the customer's latest WhatsApp message with one emoji, like a person
1841
+ does (🔍 when it starts a lookup, ✅ on the same message when done, 👍 on a thank-you that
1842
+ needs no reply; with `silent_option` on, the reaction is the whole response). One reaction
1843
+ per message, a new emoji replaces the old one. Meta Cloud API only (nothing on WATI, FlowMod
1844
+ or the other channels). Toggle in Agents → Tool Library → Emoji Reactions.
1845
+
1838
1846
  **`--tool collapse_product_variants=true` (added 4 Aug 2026).** Not a tool toggle —
1839
1847
  it changes what `get_product_info` RETURNS. OFF (the default) the result cap counts
1840
1848
  **variant rows**, so on a catalogue with several packaging/size variants per product
@@ -2299,7 +2307,9 @@ flowiq woo get <org> products --all --fields id,name,permalink,status # past t
2299
2307
  run lists subjects and contacts; `--confirm` asks you to type the org id.
2300
2308
  - **`plans create`** files the same row the MCP's `submit_plan` writes and
2301
2309
  alerts the team the same way; staff are exempt from the client lead time
2302
- (a sub-24 h send is noted, not refused).
2310
+ (a sub-24 h send is noted, not refused). On an org with
2311
+ `broadcast_planning.utility_only` (Lean Living) a `--type marketing` plan is
2312
+ refused, the same as the app and the MCP (server-side, 5 Oct 2026).
2303
2313
  - Not a CLI fix: #186 (Shopify store analytics need the `read_reports` scope
2304
2314
  on the FlowIQ app).
2305
2315
 
package/TEAM-GUIDE.md CHANGED
@@ -121,6 +121,7 @@ When you see it, run `npm i -g @flowapt/flowiq-cli` — a stale version also mea
121
121
  | Agent says an in-stock product "isn't showing" | `flowiq agent config <org_id> --tool collapse_product_variants=true` — the search cap counts VARIANT rows until this is on |
122
122
  | Agent can't quote ANY price / "I can't pull the live menu" | `flowiq agent config <org_id> --tool product_lookup=true` — name-based fuzzy lookup that works without embeddings or a live OpenAI key (semantic `get_product_info` needs both) |
123
123
  | Client wants leads/requests emailed to their team instead of a human escalation | `flowiq agent config <org_id> --tool email_request_tool=true --email-request-to a@shop.co.za,b@shop.co.za` |
124
+ | Let the agent react with an emoji (🔍 while it looks something up, ✅ when done, 👍 instead of a reply) | `flowiq agent config <org_id> --tool reaction_tool=true` (WhatsApp on the Meta API only; pair with `--tool silent_option=true` for reaction-only replies) |
124
125
  | Add extra rules to one tool (appended to its description) | `flowiq agent config <org_id> --tool-instruction send_whatsapp_message=@rules.txt` (`tool=` removes it) |
125
126
  | Turn link shortening on/off per channel | `flowiq agent config <org_id> --shorten-links web=on` (`off`, or `default` = on for WhatsApp only) |
126
127
  | Set the agent's email From name / signature | `flowiq agent config <org_id> --email-from-name "Shop Support" --email-signature-name "Laia (AI)" --email-signature-title "Customer care"` |
@@ -168,7 +169,7 @@ When you see it, run `npm i -g @flowapt/flowiq-cli` — a stale version also mea
168
169
  | Remove imported junk emails (auto-replies) across the whole inbox | `flowiq messages purge <org_id> --subject-regex '^Automatic reply' --sender-type user-email` (dry run) → `--confirm` |
169
170
  | Point an order message at a new template version, or fix its params | `flowiq org automations <org_id> --set <type> --template <name> --body param1=full_name,param2=order_number --buttons param1=short_code --commit` |
170
171
  | Reproduce what the agent or the Coworker said to a real customer | `flowiq test seed <org_id> --from <contact_id> --until "YYYY-MM-DD HH:MM" --commit`, open the seeded contact in the inbox, then `flowiq test unseed <org_id> <id> --confirm` |
171
- | File a broadcast plan for a client from the terminal | `flowiq plans create <org_id> --topic … --type utility\|marketing --date YYYY-MM-DD …` (dry run) → `--commit` |
172
+ | File a broadcast plan for a client from the terminal | `flowiq plans create <org_id> --topic … --type utility\|marketing --date YYYY-MM-DD …` (dry run) → `--commit` (utility only on orgs with `broadcast_planning.utility_only`, e.g. Lean Living) |
172
173
  | What did a tool actually return? | `flowiq messages search <org_id> "<text>" --tool-calls` |
173
174
  > **Publishing the CLI (maintainers only):** publish from a clean clone, never
174
175
  > from your working tree — `npm publish` packs whatever is on disk. A
@@ -272,6 +273,7 @@ several.
272
273
  | See what hours package a client is on | `flowiq hours package show <org_id>` (no org = every package). Setting one is Matt or Gidon only: `flowiq hours package set <org_id> --hours 5` |
273
274
  | Generate a client's monthly deck (metrics + copy) after the month has frozen | `flowiq report deck generate <org_id> 2026-08` — then review it at /reporting/deck |
274
275
  | See whether a client deck can be approved, and what is still missing | `flowiq report deck status <org_id> 2026-08` |
276
+ | Rewrite one part of a deck's copy without touching the rest (e.g. the changes column and action points) | `flowiq report deck narrate <org_id> 2026-08 --only changes,suggested_updates,action_points` |
275
277
  | Send yourself a test copy of a client deck (PDF from flowiq@flowapt.com) | `flowiq report deck send <org_id> 2026-08 --test-to you@flowapt.com` — status untouched |
276
278
  | Take a tile, pill or card off a client deck (and its PDF) for one month | On `/reporting/deck` open **Inputs** (the rail beside the slides) → Show or hide; or `flowiq report deck inputs <org_id> 2026-08 --file inputs.json` with `{ "hidden": { "tile:tail": true } }` |
277
279
  | Approve a client deck / send it to the client | `flowiq report deck approve <org_id> 2026-08` then `… send … --client` (or let the 09:00 SAST schedule send it on the 5th) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowapt/flowiq-cli",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "Command-line tool for FlowIQ staff: round-trip agent prompts, questionnaires, fine-tuning, pin-board tasks, webhooks, templates, agent-updates, chat exports, and live agent testing without ever touching service-role credentials.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -48,7 +48,7 @@ export async function flags(opts = {}) {
48
48
  if (opts.model) rows = rows.filter((r) => String(r.model || "").includes(opts.model));
49
49
  if (opts.json) { console.log(JSON.stringify({ ...resp, count: rows.length, agents: rows }, null, 2)); return; }
50
50
  const pad = (s, n) => String(s ?? "").padEnd(n);
51
- const short = { product_lookup: "lookup", ticket_tool_status: "tickets", postal_code_tool_status: "postal", restock_tool: "restock", view_cart_tool: "cart", block_tool_status: "block", email_request_tool: "email-req", shopify_products_web_chat: "web-products", woo_order_build: "woo-order", woo_tip_field: "woo-tip", woo_order_note_field: "woo-note", collapse_product_variants: "collapse", silent_option: "silent" };
51
+ const short = { product_lookup: "lookup", ticket_tool_status: "tickets", postal_code_tool_status: "postal", restock_tool: "restock", view_cart_tool: "cart", block_tool_status: "block", email_request_tool: "email-req", shopify_products_web_chat: "web-products", woo_order_build: "woo-order", woo_tip_field: "woo-tip", woo_order_note_field: "woo-note", collapse_product_variants: "collapse", silent_option: "silent", reaction_tool: "reactions" };
52
52
  console.log(`${rows.length} active agent(s)${opts.tool ? ` with ${opts.tool}` : ""}${opts.model ? ` on model ~${opts.model}` : ""}`);
53
53
  console.log("");
54
54
  console.log(` ${pad("ORG", 26)} ${pad("AGENT", 18)} ${pad("MODEL", 20)} ${pad("REASON", 7)} ${pad("PROMPT", 9)} ${pad("SW", 3)} TOOLS ON`);
@@ -73,8 +73,10 @@ export async function build(orgId, month, opts) {
73
73
 
74
74
  export async function narrate(orgId, month, opts) {
75
75
  requireArgs(orgId, month);
76
- const r = await post("narrate", orgId, month, {}, "Narrate");
76
+ const only = opts.only ? String(opts.only).split(",").map((x) => x.trim()).filter(Boolean) : null;
77
+ const r = await post("narrate", orgId, month, only ? { only } : {}, "Narrate");
77
78
  if (opts.json) { console.log(JSON.stringify(r, null, 2)); return; }
79
+ if (only) console.log(`Rewrote ${only.join(", ")} for ${r.organization?.name ?? orgId} (${month}); every other field kept as written`);
78
80
  reportCopy(r, month);
79
81
  }
80
82
 
package/src/index.js CHANGED
@@ -377,7 +377,8 @@ export function run(argv) {
377
377
  .option("--json", "raw JSON output")
378
378
  .action((orgId, month, opts) => reportCmd.build(orgId, month, opts));
379
379
  deck.command("narrate <organization_id> <month>")
380
- .description("Rewrite the ten slides' copy (LLM, under the report rules + numeric guard); hand edits are kept")
380
+ .description("Rewrite the report's copy (LLM, under the report rules + numeric guard); hand edits are kept")
381
+ .option("--only <fields>", "rewrite only these copy fields, comma-separated (e.g. changes,suggested_updates,action_points); the rest stays as written")
381
382
  .option("--json", "raw JSON output")
382
383
  .action((orgId, month, opts) => reportCmd.narrate(orgId, month, opts));
383
384
  deck.command("generate <organization_id> <month>")
@@ -618,7 +619,7 @@ export function run(argv) {
618
619
  .option("--model <model>", "settings.model (e.g. gpt-6-luna, the house default)")
619
620
  .option("--reasoning-effort <level>", "settings.reasoning_effort — low|medium|high|xhigh|max (\"\" clears it; max = GPT-6 and Claude only); pairs with reasoning models (e.g. gpt-6-luna + high, gpt-6-luna + high, claude-sonnet-5 + medium)")
620
621
  .option("--rename <name>", "rename the agent")
621
- .option("--tool <flag=bool>", "toggle a tool flag (repeatable): woo_order_build/woo_tip_field/woo_order_note_field/view_cart_tool/restock_tool/block_tool_status/postal_code_tool_status/shopify_products_web_chat/ticket_tool_status/product_lookup/email_request_tool/collapse_product_variants/silent_option", agentConfigCmd.collectTool, [])
622
+ .option("--tool <flag=bool>", "toggle a tool flag (repeatable): woo_order_build/woo_tip_field/woo_order_note_field/view_cart_tool/restock_tool/block_tool_status/postal_code_tool_status/shopify_products_web_chat/ticket_tool_status/product_lookup/email_request_tool/collapse_product_variants/silent_option/reaction_tool", agentConfigCmd.collectTool, [])
622
623
  .option("--discount [bool]", "agent.discount.enabled (true if bare)")
623
624
  .option("--test-contact-number <number>", "settings.test_contact_number — the contact `flowiq test` uses (use a FAKE number; \"\" clears it → synthetic fallback)")
624
625
  .option("--test-contact-name <name>", "settings.test_contact_name — display name for the test contact (\"\" clears it)")