@flowapt/flowiq-cli 0.12.2 → 0.12.4
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 +7 -0
- package/TEAM-GUIDE.md +4 -0
- package/package.json +1 -1
- package/src/commands/report.js +58 -0
- package/src/index.js +23 -1
package/README.md
CHANGED
|
@@ -1309,6 +1309,13 @@ flowiq report deck generate <org_id> 2026-08 --refresh --force # rebuild every
|
|
|
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
1311
|
flowiq report deck narrate <org_id> 2026-08 --only changes,action_points # rewrite just these copy fields, keep the rest as written
|
|
1312
|
+
flowiq report deck check <org_id> 2026-08 # fact-check the copy as shown (hand edits too) against the figures; rewrites nothing
|
|
1313
|
+
flowiq report deck dismiss <org_id> 2026-08 honest_read --note "stands" # keep a flagged sentence as written (recorded)
|
|
1314
|
+
flowiq report deck edit <org_id> 2026-08 honest_read --text "…" # hand-edit one copy field (--clear restores the generated text)
|
|
1315
|
+
flowiq report config <org_id> # the org's report settings
|
|
1316
|
+
flowiq report config <org_id> --set deck.org_type='"non_store"' --set deck.outcome='{"source":"handover","label_singular":"request","label_plural":"requests"}'
|
|
1317
|
+
# settings: deck.org_type, deck.outcome (handover | ticket_status | keyword | tag | link_click), deck.entry_points (in-store QR / ad pre-fills),
|
|
1318
|
+
# deck.agent_display_name, deck.client_display_name, deck.go_live_month, report.email.recipients
|
|
1312
1319
|
flowiq report deck inputs <org_id> 2026-08 --file inputs.json # the super-admin input form
|
|
1313
1320
|
flowiq report deck approve <org_id> 2026-08 # refused until every required input is present
|
|
1314
1321
|
flowiq report deck send <org_id> 2026-08 --test-to me@flowapt.com # one test copy, status untouched
|
package/TEAM-GUIDE.md
CHANGED
|
@@ -274,6 +274,10 @@ several.
|
|
|
274
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 |
|
|
275
275
|
| See whether a client deck can be approved, and what is still missing | `flowiq report deck status <org_id> 2026-08` |
|
|
276
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` |
|
|
277
|
+
| Check a deck's wording against its figures before it goes out (e.g. a heading that says a group "leads" when it does not) | `flowiq report deck check <org_id> 2026-08` — lists each unsupported statement with a fix; rewrites nothing |
|
|
278
|
+
| A fact-check flag is wrong and the sentence should stay | `flowiq report deck dismiss <org_id> 2026-08 <field> --note "why"` (or Inputs → Fact check → Keep as written) |
|
|
279
|
+
| Change the wording of one part of a deck from the terminal | `flowiq report deck edit <org_id> 2026-08 <field> --text "…"` (`--clear` puts the generated text back) |
|
|
280
|
+
| See or change a client's report settings (in-person "win", till QR codes, recipients, names) | `flowiq report config <org_id>` to see; `--set deck.outcome='{…}'`, `--set deck.entry_points='[…]'`, `--set report.email.recipients='["a@b.co"]'` to change |
|
|
277
281
|
| 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 |
|
|
278
282
|
| 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 } }` |
|
|
279
283
|
| 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.
|
|
3
|
+
"version": "0.12.4",
|
|
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": {
|
package/src/commands/report.js
CHANGED
|
@@ -80,6 +80,63 @@ export async function narrate(orgId, month, opts) {
|
|
|
80
80
|
reportCopy(r, month);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
export async function check(orgId, month, opts) {
|
|
84
|
+
requireArgs(orgId, month);
|
|
85
|
+
const r = await post("check", orgId, month, {}, "Check");
|
|
86
|
+
if (opts.json) { console.log(JSON.stringify(r, null, 2)); return; }
|
|
87
|
+
printClaims(r.issues);
|
|
88
|
+
if (r.status) console.log(` status: ${r.status}${r.missing?.length ? ` · needed: ${r.missing.join(" · ")}` : ""}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function printClaims(issues) {
|
|
92
|
+
if (!Array.isArray(issues)) return;
|
|
93
|
+
if (!issues.length) { console.log(" fact check: passed (every statement is supported by the figures)"); return; }
|
|
94
|
+
console.log(` ⚠ fact check: ${issues.length} statement${issues.length === 1 ? "" : "s"} the figures do not support:`);
|
|
95
|
+
for (const i of issues) console.log(` ${i.path}: "${i.quote}" (${i.why}) → ${i.fix}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function config(orgId, opts) {
|
|
99
|
+
if (!orgId) { console.error("Give the organization id."); process.exit(1); }
|
|
100
|
+
const pairs = opts.set ?? [];
|
|
101
|
+
let r;
|
|
102
|
+
try {
|
|
103
|
+
if (!pairs.length) r = await http.get("report", { organization_id: orgId, config: 1 });
|
|
104
|
+
else {
|
|
105
|
+
const set = {};
|
|
106
|
+
for (const p of pairs) {
|
|
107
|
+
const i = p.indexOf("=");
|
|
108
|
+
if (i < 1) { console.error(`--set needs path=value (got "${p}")`); process.exit(1); }
|
|
109
|
+
const raw = p.slice(i + 1);
|
|
110
|
+
let v; try { v = JSON.parse(raw); } catch { v = raw; }
|
|
111
|
+
set[p.slice(0, i)] = v;
|
|
112
|
+
}
|
|
113
|
+
if (opts.dryRun) { console.log(JSON.stringify({ would_set: set }, null, 2)); return; }
|
|
114
|
+
r = await http.post("report", { organization_id: orgId, action: "config_set", set });
|
|
115
|
+
}
|
|
116
|
+
} catch (e) { console.error(`Config failed: ${e.message}`); process.exit(1); }
|
|
117
|
+
if (opts.json) { console.log(JSON.stringify(r, null, 2)); return; }
|
|
118
|
+
if (r.before) for (const [k, v] of Object.entries(r.after)) console.log(`${k}: ${JSON.stringify(r.before[k])} → ${JSON.stringify(v)}`);
|
|
119
|
+
else for (const [k, v] of Object.entries(r.config)) console.log(`${k}: ${JSON.stringify(v)}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function dismiss(orgId, month, path, opts) {
|
|
123
|
+
requireArgs(orgId, month);
|
|
124
|
+
const r = await post("dismiss_claim", orgId, month, { path, ...(opts.quote ? { quote: opts.quote } : {}), ...(opts.note ? { note: opts.note } : {}) }, "Dismiss");
|
|
125
|
+
if (opts.json) { console.log(JSON.stringify(r, null, 2)); return; }
|
|
126
|
+
console.log(`Kept as written: ${r.dismissed} fact-check flag${r.dismissed === 1 ? "" : "s"} on ${path}`);
|
|
127
|
+
if (r.status) console.log(` status: ${r.status}${r.missing?.length ? ` · needed: ${r.missing.join(" · ")}` : ""}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function edit(orgId, month, path, opts) {
|
|
131
|
+
requireArgs(orgId, month);
|
|
132
|
+
const text = opts.clear ? "" : opts.text;
|
|
133
|
+
if (typeof text !== "string") { console.error("Give --text \"…\" (or --clear to put the generated wording back)."); process.exit(1); }
|
|
134
|
+
const r = await post("save_override", orgId, month, { path, text }, "Edit");
|
|
135
|
+
if (opts.json) { console.log(JSON.stringify(r, null, 2)); return; }
|
|
136
|
+
console.log(r.cleared ? `Generated wording restored on ${path}` : `Saved your wording on ${path}`);
|
|
137
|
+
if (r.status) console.log(` status: ${r.status}${r.missing?.length ? ` · needed: ${r.missing.join(" · ")}` : ""}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
83
140
|
export async function generate(orgId, month, opts) {
|
|
84
141
|
requireArgs(orgId, month);
|
|
85
142
|
const r = await post("generate", orgId, month, { refresh: !!opts.refresh, force: !!opts.force }, "Generate");
|
|
@@ -94,6 +151,7 @@ function reportCopy(r, month) {
|
|
|
94
151
|
const n = Object.keys(r.guard.offenders || {}).length;
|
|
95
152
|
console.log(` ⚠ numeric guard: ${n} field${n === 1 ? "" : "s"} carry figures that are not in the data — flagged on the slides: ${Object.keys(r.guard.offenders).join(", ")}`);
|
|
96
153
|
} else if (r.guard) console.log(" numeric guard: passed (every figure in the copy exists in the data)");
|
|
154
|
+
if (r.guard?.claim_check?.ran) printClaims(r.guard.claim_check.issues);
|
|
97
155
|
if (Array.isArray(r.length_issues) && r.length_issues.length) console.log(` trimmed to limit: ${r.length_issues.map((i) => `${i.path} (${i.length}→${i.limit})`).join(", ")}`);
|
|
98
156
|
}
|
|
99
157
|
|
package/src/index.js
CHANGED
|
@@ -365,7 +365,7 @@ export function run(argv) {
|
|
|
365
365
|
.action((orgId, opts) => hoursCmd.packageClear(orgId, opts));
|
|
366
366
|
|
|
367
367
|
// report deck — the monthly client deck (build / copy / approve / send)
|
|
368
|
-
const report = program.command("report").description("Client reporting: the monthly
|
|
368
|
+
const report = program.command("report").description("Client reporting: the monthly client deck (a cover and five slides) and its settings");
|
|
369
369
|
const deck = report.command("deck").description("The monthly client deck for one org and month (/reporting/deck)");
|
|
370
370
|
deck.command("status <organization_id> <month>")
|
|
371
371
|
.description("What exists for the month, the workflow status and what still blocks approval")
|
|
@@ -381,6 +381,28 @@ export function run(argv) {
|
|
|
381
381
|
.option("--only <fields>", "rewrite only these copy fields, comma-separated (e.g. changes,suggested_updates,action_points); the rest stays as written")
|
|
382
382
|
.option("--json", "raw JSON output")
|
|
383
383
|
.action((orgId, month, opts) => reportCmd.narrate(orgId, month, opts));
|
|
384
|
+
deck.command("check <organization_id> <month>")
|
|
385
|
+
.description("Fact-check the copy as the slides show it (hand edits included) against the month's figures; rewrites nothing")
|
|
386
|
+
.option("--json", "raw JSON output")
|
|
387
|
+
.action((orgId, month, opts) => reportCmd.check(orgId, month, opts));
|
|
388
|
+
report.command("config <organization_id>")
|
|
389
|
+
.description("Show or change an org's report settings: deck.org_type, deck.outcome, deck.entry_points, deck.agent_display_name, deck.client_display_name, deck.go_live_month, report.email.recipients")
|
|
390
|
+
.option("--set <path=json>", "a setting to change, JSON value (repeatable), e.g. --set deck.org_type='\"non_store\"'", (v, acc) => [...acc, v], [])
|
|
391
|
+
.option("--dry-run", "print what would be set")
|
|
392
|
+
.option("--json", "raw JSON output")
|
|
393
|
+
.action((orgId, opts) => reportCmd.config(orgId, opts));
|
|
394
|
+
deck.command("dismiss <organization_id> <month> <path>")
|
|
395
|
+
.description("Keep a sentence as written: drop the fact-check flag(s) on one copy field (who and when are recorded)")
|
|
396
|
+
.option("--quote <words>", "only the flag quoting these words (default: every open flag on the field)")
|
|
397
|
+
.option("--note <text>", "why it stands")
|
|
398
|
+
.option("--json", "raw JSON output")
|
|
399
|
+
.action((orgId, month, path, opts) => reportCmd.dismiss(orgId, month, path, opts));
|
|
400
|
+
deck.command("edit <organization_id> <month> <path>")
|
|
401
|
+
.description("Hand-edit one copy field (e.g. honest_read, plan.agent_upgrades); shows on the slide as an edit with your name")
|
|
402
|
+
.option("--text <text>", "the new wording")
|
|
403
|
+
.option("--clear", "put the generated wording back")
|
|
404
|
+
.option("--json", "raw JSON output")
|
|
405
|
+
.action((orgId, month, path, opts) => reportCmd.edit(orgId, month, path, opts));
|
|
384
406
|
deck.command("generate <organization_id> <month>")
|
|
385
407
|
.description("Build the metrics (if missing, or --refresh) and write the copy")
|
|
386
408
|
.option("--refresh", "rebuild the metrics first")
|