@wrangled/cli 0.3.22 → 0.3.23
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 +3 -2
- package/dist/main.js +23 -7
- package/package.json +1 -1
package/dist/guide.js
CHANGED
|
@@ -35,9 +35,10 @@ To read the whole library with your own tools, such as "every email we have" or
|
|
|
35
35
|
|
|
36
36
|
## Know-how library: Creative Cartel
|
|
37
37
|
|
|
38
|
-
For what has worked for others, such as "how do members test new hooks" or "what do people say about advertorials", search the know-how library: \`${cli} knowledge search "<question>"\`. Today it holds Creative Cartel's library (\`creative-cartel\`)
|
|
38
|
+
For what has worked for others, such as "how do members test new hooks" or "what do people say about advertorials", search the know-how library: \`${cli} knowledge search "<question>"\`. Today it holds Creative Cartel's library (\`creative-cartel\`) the team's own Exodus copy and ad frameworks (\`exodus\`), and Meta's advertising standards (\`meta-policy\`), all copied into this server every Monday afternoon. It works only when the key has library access (\`${cli} whoami\` says); without it the answer is \`knowledge_not_allowed\`. It finds snippets by their words and by their meaning, and returns them short, never whole documents: each at most 280 characters, with its title, link, kind, caveat, evidence and the day its library was copied in. Evidence \`reported member statement\` is a member's claim, not a checked result; weigh it that way and say so when you use it. Evidence \`team method\` is the team's own framework: reason with it, never paste it into client work. The library writes no answer for you: read the snippets and decide. Use your own words: saving text that repeats 12 words in a row from anything in the library is refused with \`copies_library_text\`.
|
|
39
39
|
|
|
40
40
|
- The question is 1 to 4000 characters. \`--limit\` is 1 to 12 (default 5). \`--kind\` (message, transcript, document, note) keeps one kind of source. \`--library <slug>\` keeps one library.
|
|
41
|
+
- \`--library meta-policy\` searches Meta's own advertising standards, copied from Meta's site every Monday. Before you write or judge an ad, check its claims there, such as income, health or before-and-after claims: \`${cli} knowledge search --library meta-policy "income claims"\`. Its evidence is \`meta_published_policy\`, Meta's published rule; Meta's reviewers still decide each ad.
|
|
41
42
|
- \`knowledge_not_synced\` means the library has not been copied into this server yet, so nothing was searched; the weekly sync fills it.
|
|
42
43
|
|
|
43
44
|
## Query workflow: schema first
|
|
@@ -190,7 +191,7 @@ Tag ads, emails, texts and pages only with the words \`${cli} grammar\` lists (\
|
|
|
190
191
|
|
|
191
192
|
## What customers keep saying
|
|
192
193
|
|
|
193
|
-
Every hour an AI reads the workspace's
|
|
194
|
+
Every hour an AI reads what is new in the workspace's Facebook and Instagram comments, Google reviews, Reddit posts and comments, recorded calls and meetings (Fathom, Zoom, CallRail) and chats (GoHighLevel texts and chat, Closebot), pulls out openers (lines that would open an ad), objections (why people hesitate), pains (problems they live with), desires (results they want) and phrases (exact wording worth copying into an ad), and groups the ones that say the same thing. From calls and chats only the customer's words count, and names, emails, phone numbers and handles are taken out first. \`${cli} voice\` lists the groups, most common first (\`--kind opener|objection|pain|desire|phrase\`, \`--limit <n>\` up to 200, \`--json\`), each with how many sources said it (a conversation counts once), their likes, and up to 3 real quotes. \`${cli} voice --since <YYYY-MM-DD> --sort useful\` lists single lines said since that day instead, the most usable in an ad first: specific, first-person, recent and often said; \`--json\` adds each line's link. In \`--json\`, each quote's \`kind\` (a line's \`source\`) and \`ref\` go straight into a creative's \`researchSources\`, and a group itself can be cited as its \`ref\`, \`voice_clusters:<id>\`. The same rows are in the \`voice_clusters\` and \`voice_insights\` tables.
|
|
194
195
|
|
|
195
196
|
## The weekly story
|
|
196
197
|
|
package/dist/main.js
CHANGED
|
@@ -15,6 +15,7 @@ import { SKILL_NAME_PATTERN } from "./skill-zip.js";
|
|
|
15
15
|
import { installSkillFolder, operatorConnection, packSkill, parseSkillRef, readSkillFolder, unpackSkill, } from "./skills.js";
|
|
16
16
|
const DEFAULT_WAIT_SECONDS = 300;
|
|
17
17
|
const SERVER_WAIT_CAP = 50;
|
|
18
|
+
const VOICE_USAGE = "voice [--kind opener|objection|pain|desire|phrase] [--since <YYYY-MM-DD>] [--sort common|useful] [--limit <n>] [--json]";
|
|
18
19
|
const commands = {
|
|
19
20
|
login: {
|
|
20
21
|
usage: `login (paste the key when asked, pipe it on stdin, or set ${envPrefix}_API_KEY)`,
|
|
@@ -1125,21 +1126,30 @@ const commands = {
|
|
|
1125
1126
|
},
|
|
1126
1127
|
},
|
|
1127
1128
|
voice: {
|
|
1128
|
-
usage:
|
|
1129
|
+
usage: `${VOICE_USAGE} what customers keep saying, most common first, or with --sort useful the most usable single lines`,
|
|
1129
1130
|
async run(args, options) {
|
|
1130
1131
|
if (args.length > 0)
|
|
1131
|
-
throw new CliError(
|
|
1132
|
+
throw new CliError(`Usage: ${VOICE_USAGE}`, exitCodes.usage);
|
|
1132
1133
|
const query = new URLSearchParams();
|
|
1133
1134
|
if (options.kind)
|
|
1134
1135
|
query.set("kind", options.kind);
|
|
1136
|
+
if (options.since)
|
|
1137
|
+
query.set("since", options.since);
|
|
1138
|
+
if (options.sort)
|
|
1139
|
+
query.set("sort", options.sort);
|
|
1135
1140
|
if (options.limit)
|
|
1136
1141
|
query.set("limit", options.limit);
|
|
1137
|
-
const
|
|
1142
|
+
const read = await api(await resolveConnection(options.apiUrl), `${VOICE_PATH}${query.size ? `?${query}` : ""}`);
|
|
1138
1143
|
if (options.format === "json")
|
|
1139
|
-
return printJson(
|
|
1140
|
-
if (
|
|
1144
|
+
return printJson(read);
|
|
1145
|
+
if ("lines" in read) {
|
|
1146
|
+
if (read.lines.length === 0)
|
|
1147
|
+
return console.log("Nothing said in that time yet.");
|
|
1148
|
+
return console.log(renderTable(VOICE_LINE_COLUMNS, read.lines.map(voiceLineRow)));
|
|
1149
|
+
}
|
|
1150
|
+
if (read.clusters.length === 0)
|
|
1141
1151
|
return console.log("Nothing grouped yet.");
|
|
1142
|
-
console.log(renderTable(VOICE_COLUMNS, clusters.map(voiceRow)));
|
|
1152
|
+
console.log(renderTable(VOICE_COLUMNS, read.clusters.map(voiceRow)));
|
|
1143
1153
|
},
|
|
1144
1154
|
},
|
|
1145
1155
|
"ads tags": {
|
|
@@ -1798,6 +1808,7 @@ const VOICE_SUGGESTIONS_PATH = "/api/v1/voice-lists/suggestions";
|
|
|
1798
1808
|
const FACT_COLUMNS = ["id", "kind", "subject", "key", "status", "summary"];
|
|
1799
1809
|
const VOICE_PATH = "/api/v1/voice";
|
|
1800
1810
|
const VOICE_COLUMNS = ["ref", "kind", "said by", "likes", "label", "top quote"];
|
|
1811
|
+
const VOICE_LINE_COLUMNS = ["date", "kind", "source", "quote", "ref"];
|
|
1801
1812
|
function knowledgeBlock(r, i) {
|
|
1802
1813
|
const lines = [`${i + 1}. ${r.title}`, ` ${r.kind}, ${r.evidence.replaceAll("_", " ")} (${r.source}${r.syncedAt ? `, copied in ${r.syncedAt.slice(0, 10)}` : ""})`];
|
|
1803
1814
|
if (r.url)
|
|
@@ -1818,6 +1829,9 @@ function contentTagRow(t) {
|
|
|
1818
1829
|
function voiceRow(c) {
|
|
1819
1830
|
return { ref: c.ref, kind: c.kind, "said by": c.supportCount, likes: c.likeSum, label: excerpt(c.label, 60), "top quote": c.evidence[0] ? excerpt(c.evidence[0].quote, 60) : null };
|
|
1820
1831
|
}
|
|
1832
|
+
function voiceLineRow(l) {
|
|
1833
|
+
return { date: l.date.slice(0, 10), kind: l.kind, source: l.source, quote: excerpt(l.quote, 80), ref: l.ref };
|
|
1834
|
+
}
|
|
1821
1835
|
function factRow(f) {
|
|
1822
1836
|
return { id: f.id, kind: f.kind, subject: `${f.subjectType}:${f.subjectRef}`, key: f.key, status: f.status, summary: excerpt(f.summary, 80) };
|
|
1823
1837
|
}
|
|
@@ -1934,7 +1948,7 @@ Options:
|
|
|
1934
1948
|
--order desc|asc content search's sort direction (default: desc, best or newest first)
|
|
1935
1949
|
--limit <n> how many results content search, creatives list, campaigns, voice, ads tags, content tags and content to-check
|
|
1936
1950
|
return, or how many ads ads autotag tags and how many items content autotag tags
|
|
1937
|
-
--library <slug> with knowledge search, only this library: creative-cartel or
|
|
1951
|
+
--library <slug> with knowledge search, only this library: creative-cartel, exodus or meta-policy
|
|
1938
1952
|
--ad <id> with ads tags, only this Meta ad's tags; with ads autotag, only this ad
|
|
1939
1953
|
--kind email|text|page with content tags and content autotag, only that kind (content autotag takes a comma list)
|
|
1940
1954
|
--max-seconds <n> stop a runner's run after this many seconds, if that comes before its deadline
|
|
@@ -1987,6 +2001,7 @@ function parseOptions(argv) {
|
|
|
1987
2001
|
force: { type: "boolean", default: false },
|
|
1988
2002
|
yes: { type: "boolean", default: false },
|
|
1989
2003
|
from: { type: "string" },
|
|
2004
|
+
since: { type: "string" },
|
|
1990
2005
|
to: { type: "string" },
|
|
1991
2006
|
grain: { type: "string" },
|
|
1992
2007
|
channel: { type: "string" },
|
|
@@ -2082,6 +2097,7 @@ function parseOptions(argv) {
|
|
|
2082
2097
|
force: values.force,
|
|
2083
2098
|
yes: values.yes,
|
|
2084
2099
|
from: values.from,
|
|
2100
|
+
since: values.since,
|
|
2085
2101
|
to: values.to,
|
|
2086
2102
|
grain: values.grain,
|
|
2087
2103
|
channel: values.channel,
|