@wrangled/cli 0.3.28 → 0.3.29
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/guide.js +5 -0
- package/dist/main.js +48 -0
- package/package.json +1 -1
package/dist/guide.js
CHANGED
|
@@ -30,6 +30,10 @@ Lessons are short plain sentences about why the winners won and the flops floppe
|
|
|
30
30
|
|
|
31
31
|
Write one as JSON, \`{"text": "...", "dimension": "hook", "evidence": [{"ad": "<Meta ad id or SKU>", "numbers": "cost per sale $180 vs $410"}], "week": "2026-09-28"}\`, and run \`${cli} lessons add --file lesson.json\`. \`dimension\` (hook, angle, format, offer, persona or other) and \`week\` (any day of the winners report's week; default the latest complete week) are optional. A lesson with the same words that still holds, or the one named in \`"reinforces": "<id>"\`, is reinforced instead of copied: it counts once more for a week it had not counted, so running the same week again changes no count. \`${cli} lessons retire <id> --note "<why>"\` retires one the numbers no longer back; it is kept with why. The \`lessons\` playbook (\`${cli} skill install lessons\` installs it as the \`${LESSONS_SKILL_NAME}\` skill) writes the week's lessons from the winners report.
|
|
32
32
|
|
|
33
|
+
## Sending winners to Exodus
|
|
34
|
+
|
|
35
|
+
\`${cli} winners send-to-exodus\` sends the week's winners to Exodus so it makes new ads from them: one package per Facebook page, each winner with its reason, its numbers, and a note of the lessons it proves, persona lessons and the week's freshest customer words. Winners whose page, words or format are unknown are left out and listed with why. \`--week <YYYY-MM-DD>\` picks the week (default: the latest complete week). \`--dry-run\` asks Exodus what it would create or update and writes nothing. Sending the same week again is safe: an unchanged package is not sent twice, and Exodus updates each ad in place. A real send needs the workspace's Exodus brand and a Scrape Creators key on the Exodus account.
|
|
36
|
+
|
|
33
37
|
## Content: search before SQL
|
|
34
38
|
|
|
35
39
|
To find content, such as "the 10 emails with the best click rate", "our 5 best videos by return on ad spend" or "every landing page that mentions the guarantee", use \`${cli} content search\` before writing SQL on the \`content_\` tables.
|
|
@@ -257,6 +261,7 @@ ${cli} knowledge tag <entry-id> --hook-kind <v> --confidence <c> tag a library
|
|
|
257
261
|
${cli} guide what this workspace's data holds, how fresh it is, and what is missing: read it first
|
|
258
262
|
${cli} brief [--from <date>] [--to <date>] [--channel <channel>] [--campaign <id>] [--top <n>] the offer, voice, best and worst ads, pages, results and audience: start here before writing ads
|
|
259
263
|
${cli} winners [--week <date>] [--top <n>] which Facebook ads won and flopped this week and over 30 days, judged on first-touch sales
|
|
264
|
+
${cli} winners send-to-exodus [--week <date>] [--dry-run] send the week's winners, with their reasons, lessons and customer words, to Exodus; safe to send again
|
|
260
265
|
${cli} warehouse schema [--summary] [--table <name> ...] [--search <words>] list tables and columns; --summary for one row per table
|
|
261
266
|
${cli} warehouse query "<sql>" run one read-only query (or --file <path>, or stdin); --out <file.csv | file.ndjson | file.json> saves the rows; add --all for every row
|
|
262
267
|
${cli} funnel [--from <date>] [--to <date>] [--grain day|week|month] spend, leads, calls and sales per period, with totals
|
package/dist/main.js
CHANGED
|
@@ -657,6 +657,24 @@ const commands = {
|
|
|
657
657
|
process.stdout.write(winnersText(report));
|
|
658
658
|
},
|
|
659
659
|
},
|
|
660
|
+
"winners send-to-exodus": {
|
|
661
|
+
usage: "winners send-to-exodus [--week <YYYY-MM-DD>] [--dry-run] send the week's winning ads, with their reasons, lessons and customer words, to Exodus; safe to send again",
|
|
662
|
+
async run(args, options) {
|
|
663
|
+
if (args.length > 0)
|
|
664
|
+
throw new CliError("Usage: winners send-to-exodus [--week <date>] [--dry-run]", exitCodes.usage);
|
|
665
|
+
const sent = await api(await resolveConnection(options.apiUrl), "/api/v1/winners/exodus", {
|
|
666
|
+
...(options.week === undefined ? {} : { week: options.week }),
|
|
667
|
+
dryRun: options.dryRun,
|
|
668
|
+
});
|
|
669
|
+
if (options.format === "json")
|
|
670
|
+
printJson(sent);
|
|
671
|
+
else
|
|
672
|
+
process.stdout.write(winnersSendText(sent));
|
|
673
|
+
const failed = sent.pages.filter((p) => p.run.status !== "succeeded").length;
|
|
674
|
+
if (failed > 0)
|
|
675
|
+
throw new CliError(`${failed} of ${sent.pages.length} ${sent.pages.length === 1 ? "page was" : "pages were"} not sent; see above.`, exitCodes.error);
|
|
676
|
+
},
|
|
677
|
+
},
|
|
660
678
|
examples: {
|
|
661
679
|
usage: "examples (plain questions with SQL that answers them, for the tables this workspace has)",
|
|
662
680
|
async run(args, options) {
|
|
@@ -1913,6 +1931,34 @@ function winnersWindowText(title, window) {
|
|
|
1913
1931
|
.join(""));
|
|
1914
1932
|
}
|
|
1915
1933
|
/** The winners report as plain Markdown: what a sale means, then this week and the last 30 days, each currency apart. */
|
|
1934
|
+
function winnersSendText(sent) {
|
|
1935
|
+
const lines = [`Week ${sent.week.from} to ${sent.week.to}${sent.dryRun ? ", dry run: nothing was written to Exodus" : ""}.`];
|
|
1936
|
+
if (sent.pages.length === 0)
|
|
1937
|
+
lines.push("No winners to send.");
|
|
1938
|
+
for (const page of sent.pages) {
|
|
1939
|
+
lines.push(`${page.pageName} (page ${page.pageId}, ${page.adAccountId}): ${page.winners} ${page.winners === 1 ? "winner" : "winners"}`);
|
|
1940
|
+
const answer = page.exodus;
|
|
1941
|
+
if (answer === null)
|
|
1942
|
+
lines.push(` Not sent: ${page.run.error ?? `the run is ${page.run.status}`}`);
|
|
1943
|
+
else if ("dryRun" in answer) {
|
|
1944
|
+
for (const action of ["create", "update"]) {
|
|
1945
|
+
const ads = answer.perWinner.filter((w) => w.action === action).map((w) => w.accountAdId);
|
|
1946
|
+
if (ads.length > 0)
|
|
1947
|
+
lines.push(` Would ${action}: ${ads.join(", ")}`);
|
|
1948
|
+
}
|
|
1949
|
+
if (answer.scrapecreatorsKey === "missing")
|
|
1950
|
+
lines.push(" Exodus has no Scrape Creators key, so a real send would fail. Add it in Exodus under Settings, Keys.");
|
|
1951
|
+
}
|
|
1952
|
+
else {
|
|
1953
|
+
lines.push(` ${page.alreadySent ? "Already sent; not sent again" : "Sent"}: import ${answer.importId}, ${answer.accepted} accepted, ${answer.rejected.length} rejected`);
|
|
1954
|
+
}
|
|
1955
|
+
for (const r of answer?.rejected ?? [])
|
|
1956
|
+
lines.push(` Rejected ${r.accountAdId}: ${r.reason}`);
|
|
1957
|
+
}
|
|
1958
|
+
if (sent.skipped.length > 0)
|
|
1959
|
+
lines.push("Left out:", ...sent.skipped.map((s) => ` ${s.adId}: ${s.reason}`));
|
|
1960
|
+
return `${lines.join("\n")}\n`;
|
|
1961
|
+
}
|
|
1916
1962
|
function winnersText(report) {
|
|
1917
1963
|
return (`# Winners and flops for ${report.workspace}\n\n` +
|
|
1918
1964
|
`A sale means ${report.saleMeans}. Each sale goes to the Facebook ad that first brought the buyer in; payments typed into GoHighLevel by hand never count toward an ad.\n` +
|
|
@@ -2247,6 +2293,7 @@ function parseOptions(argv) {
|
|
|
2247
2293
|
awareness: { type: "string" },
|
|
2248
2294
|
confidence: { type: "string" },
|
|
2249
2295
|
remove: { type: "boolean", default: false },
|
|
2296
|
+
"dry-run": { type: "boolean", default: false },
|
|
2250
2297
|
help: { type: "boolean", short: "h", default: false },
|
|
2251
2298
|
},
|
|
2252
2299
|
});
|
|
@@ -2347,6 +2394,7 @@ function parseOptions(argv) {
|
|
|
2347
2394
|
entryTags: Object.fromEntries(Object.entries(entryTagFlags).flatMap(([field, flag]) => (values[flag] === undefined ? [] : [[field, values[flag]]]))),
|
|
2348
2395
|
confidence: values.confidence,
|
|
2349
2396
|
remove: values.remove,
|
|
2397
|
+
dryRun: values["dry-run"],
|
|
2350
2398
|
},
|
|
2351
2399
|
};
|
|
2352
2400
|
}
|