marketing-mindset 1.3.1 → 1.4.0
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 +8 -0
- package/bin/cli.js +71 -1
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -59,3 +59,11 @@ The same checklist is a browser tool with a live score:
|
|
|
59
59
|
<https://axelfreeman.github.io/marketing-mindset/tools/aeo-readiness-checklist.html>.
|
|
60
60
|
If you want the plumbing in that checklist built for you (offer page, query-intent pages, schema,
|
|
61
61
|
`llms.txt`, IndexNow, the tracked event), packages are published at https://axelfreeman.com/marketing-engineer.html.
|
|
62
|
+
|
|
63
|
+
### Sending ramp: `warmup`
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
npx -y marketing-mindset@1.4.0 warmup --start 2026-09-21 --target 200 --days 21 --mailboxes 4
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Day-by-day ramp for a new sending domain: starts at 20/day (or the target, if smaller), grows by half a day, never above the target, and prints the four stop rules (bounce >2%/day, complaints >0.1%, any reply ends that sequence, opens halving with flat sends -> drop back two ramp days). `--json` gives the machine-readable schedule. Work with me: https://axelfreeman.com/marketing-engineer.html
|
package/bin/cli.js
CHANGED
|
@@ -31,7 +31,7 @@ function flags(argv) {
|
|
|
31
31
|
argv.forEach((a, i) => {
|
|
32
32
|
if (a.startsWith("--")) {
|
|
33
33
|
const v = argv[i + 1];
|
|
34
|
-
out[a.slice(2)] = v && !v.startsWith("--") ? Number(v) : true;
|
|
34
|
+
out[a.slice(2)] = v && !v.startsWith("--") ? (isNaN(Number(v)) ? v : Number(v)) : true;
|
|
35
35
|
}
|
|
36
36
|
});
|
|
37
37
|
return out;
|
|
@@ -167,6 +167,72 @@ function runJd(opts) {
|
|
|
167
167
|
return 0;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
/* warmup — day-by-day sending ramp. Deterministic: same inputs, same schedule. */
|
|
171
|
+
function runWarmup(opts) {
|
|
172
|
+
const start = opts.start === undefined ? "" : String(opts.start);
|
|
173
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(start) || isNaN(Date.parse(start))) {
|
|
174
|
+
console.error("warmup: --start must be YYYY-MM-DD (got: " + JSON.stringify(start) + ")");
|
|
175
|
+
return 1;
|
|
176
|
+
}
|
|
177
|
+
const target = Number(opts.target === undefined ? 0 : opts.target);
|
|
178
|
+
if (!isFinite(target) || target <= 0) {
|
|
179
|
+
console.error("warmup: --target must be a positive number of sends per day (got: " + JSON.stringify(opts.target) + ")");
|
|
180
|
+
return 1;
|
|
181
|
+
}
|
|
182
|
+
const days = Number(opts.days === undefined ? 21 : opts.days);
|
|
183
|
+
if (!isFinite(days) || days < 1 || days > 180) {
|
|
184
|
+
console.error("warmup: --days must be between 1 and 180 (got: " + JSON.stringify(opts.days) + ")");
|
|
185
|
+
return 1;
|
|
186
|
+
}
|
|
187
|
+
const mailboxes = Number(opts.mailboxes === undefined ? 1 : opts.mailboxes);
|
|
188
|
+
if (!isFinite(mailboxes) || mailboxes < 1) {
|
|
189
|
+
console.error("warmup: --mailboxes must be >= 1");
|
|
190
|
+
return 1;
|
|
191
|
+
}
|
|
192
|
+
const date0 = new Date(start + "T00:00:00Z");
|
|
193
|
+
const rows = [];
|
|
194
|
+
let prev = Math.min(20, target);
|
|
195
|
+
let cumulative = 0;
|
|
196
|
+
let reached = 0;
|
|
197
|
+
for (let i = 0; i < days; i++) {
|
|
198
|
+
const vol = i === 0 ? prev : Math.min(target, Math.max(prev, Math.round(prev * 1.5)));
|
|
199
|
+
const d = new Date(date0.getTime() + i * 86400000);
|
|
200
|
+
if (vol >= target && !reached) reached = i + 1;
|
|
201
|
+
cumulative += vol;
|
|
202
|
+
rows.push({ day: i + 1, date: d.toISOString().slice(0, 10), sends: vol, cumulative: cumulative });
|
|
203
|
+
prev = vol;
|
|
204
|
+
}
|
|
205
|
+
const perMailbox = Math.round((target / mailboxes) * 10) / 10;
|
|
206
|
+
if (opts.json) {
|
|
207
|
+
console.log(JSON.stringify({
|
|
208
|
+
start: start, target_per_day: target, days: days, mailboxes: mailboxes,
|
|
209
|
+
first_day: rows[0].sends, full_volume_day: reached || null,
|
|
210
|
+
total_sends: cumulative, per_mailbox_at_target: perMailbox,
|
|
211
|
+
schedule: rows,
|
|
212
|
+
stop_rules: [
|
|
213
|
+
"hard bounce rate over 2% in a day: stop the day, find the source",
|
|
214
|
+
"spam complaint over 0.1%: stop the campaign, not the day",
|
|
215
|
+
"any reply: the sequence stops for that person, replied is the goal",
|
|
216
|
+
"deliverability dip (opens down by half with sends flat): drop back two ramp days"
|
|
217
|
+
]
|
|
218
|
+
}, null, 2));
|
|
219
|
+
return 0;
|
|
220
|
+
}
|
|
221
|
+
console.log("Warmup plan: start " + start + ", target " + target + " sends/day over " + days + " days"
|
|
222
|
+
+ (mailboxes > 1 ? ", " + mailboxes + " mailboxes (" + perMailbox + "/mailbox/day)" : ""));
|
|
223
|
+
console.log("day date sends cumulative");
|
|
224
|
+
rows.forEach(function (r) {
|
|
225
|
+
console.log(String(r.day).padStart(3) + " " + r.date + " " + String(r.sends).padStart(5)
|
|
226
|
+
+ " " + String(r.cumulative).padStart(10));
|
|
227
|
+
});
|
|
228
|
+
console.log("day 1 starts at " + rows[0].sends + "; full volume on day " + (reached || "not reached within --days")
|
|
229
|
+
+ "; " + cumulative + " sends total in the window.");
|
|
230
|
+
console.log("Stop rules: bounce >2%/day -> stop the day; complaints >0.1% -> stop the campaign;"
|
|
231
|
+
+ " any reply -> that person's sequence ends; opens halving with flat sends -> drop back two ramp days.");
|
|
232
|
+
console.log("-- Method and packages: https://axelfreeman.com/marketing-engineer.html");
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
235
|
+
|
|
170
236
|
const cmd = (process.argv[2] || "").toLowerCase();
|
|
171
237
|
const rest = process.argv.slice(3);
|
|
172
238
|
|
|
@@ -201,6 +267,8 @@ if (cmd === "sample") {
|
|
|
201
267
|
}, null, 2));
|
|
202
268
|
} else if (cmd === "aeo") {
|
|
203
269
|
process.exit(runAeo(flags(rest)));
|
|
270
|
+
} else if (cmd === "warmup") {
|
|
271
|
+
process.exit(runWarmup(flags(rest)));
|
|
204
272
|
} else if (cmd === "jd") {
|
|
205
273
|
process.exit(runJd(flags(rest)));
|
|
206
274
|
} else if (cmd === "sections") {
|
|
@@ -225,6 +293,8 @@ Usage:
|
|
|
225
293
|
--base 0.03 --lift 0.2 --power 0.8 --alpha 0.05
|
|
226
294
|
npx marketing-mindset kill kill rule for a running test
|
|
227
295
|
--base 0.03 --n 1500 --lift 0.2
|
|
296
|
+
npx marketing-mindset warmup day-by-day sending ramp with stop rules
|
|
297
|
+
--start 2026-09-21 --target 200 --days 21 --mailboxes 4
|
|
228
298
|
npx marketing-mindset jd marketing engineer job description
|
|
229
299
|
--level senior --mode fulltime --surfaces seo,out,analytics
|
|
230
300
|
npx marketing-mindset aeo agent-readiness / AEO checklist (23 weighted checks)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "marketing-mindset",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "A marketer's operating mindset as an installable agent skill and CLI: test-size floors, kill rules, agent-readiness checklist, marketing-engineer job description. npx marketing-mindset sample|kill|aeo|jd|sections|read|install.",
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "A marketer's operating mindset as an installable agent skill and CLI: test-size floors, kill rules, agent-readiness checklist, marketing-engineer job description. npx marketing-mindset sample|kill|warmup|aeo|jd|sections|read|install.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-skill",
|
|
7
7
|
"skill",
|
|
@@ -13,7 +13,10 @@
|
|
|
13
13
|
"llm",
|
|
14
14
|
"prompt",
|
|
15
15
|
"sample-size",
|
|
16
|
-
"job-description"
|
|
16
|
+
"job-description",
|
|
17
|
+
"cold-email",
|
|
18
|
+
"deliverability",
|
|
19
|
+
"warmup"
|
|
17
20
|
],
|
|
18
21
|
"license": "MIT",
|
|
19
22
|
"author": "Axel Freeman",
|