marketing-mindset 1.0.0 → 1.1.1
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 +43 -0
- package/bin/cli.js +129 -0
- package/package.json +4 -3
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# marketing-mindset
|
|
2
|
+
|
|
3
|
+
A marketer's operating mindset, packaged as an installable agent skill plus a CLI.
|
|
4
|
+
Not a template library: the judgement layer — how much volume a test needs before a verdict is allowed,
|
|
5
|
+
when to kill a channel, what a deliverable is.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx marketing-mindset # help + the test-size floors
|
|
9
|
+
npx marketing-mindset sample --base 0.03 --lift 0.2 # required per-arm sample size
|
|
10
|
+
npx marketing-mindset kill --base 0.03 --n 1500 # the kill rule for a running test
|
|
11
|
+
npx marketing-mindset sections # list every section of SKILL.md
|
|
12
|
+
npx marketing-mindset read numbers # print one section
|
|
13
|
+
npx marketing-mindset install # copy the skill into your agent's skills dir
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Floors that do not move
|
|
17
|
+
|
|
18
|
+
| Test | Volume before a verdict means anything |
|
|
19
|
+
|---|---|
|
|
20
|
+
| Cold email reply rate | ~1,500–2,000 sends per variant |
|
|
21
|
+
| Landing page smoke test | 100–200 targeted visitors |
|
|
22
|
+
| Strict A/B test | ~10,000 visitors per variation |
|
|
23
|
+
| Paid ads | spend gate at 1–3x target CPA, 48–72h |
|
|
24
|
+
|
|
25
|
+
## Links
|
|
26
|
+
|
|
27
|
+
- Skill page: https://axelfreeman.github.io/marketing-mindset/
|
|
28
|
+
- Source: https://github.com/axelfreeman/marketing-mindset
|
|
29
|
+
- Free test-size calculator (browser, no signup): https://axelfreeman.github.io/marketing-mindset/tools/email-test-planner.html
|
|
30
|
+
- MCP server, same method over stdio: https://www.npmjs.com/package/marketing-mindset-mcp
|
|
31
|
+
- `llms.txt` for agents: https://axelfreeman.com/llms.txt
|
|
32
|
+
|
|
33
|
+
## Need this done for you?
|
|
34
|
+
|
|
35
|
+
The skill is the method. The done-for-you version — offer, landing pages, a free tool,
|
|
36
|
+
sending setup that passes a deliverability checklist, tests with a written kill rule, and
|
|
37
|
+
measurement inside your own analytics property — is sold as fixed-scope packages, with the
|
|
38
|
+
scope and the prices published before you get on a call:
|
|
39
|
+
|
|
40
|
+
**https://axelfreeman.com/marketing-engineer.html** — Sprint $900 · Engine $1,900/month · Full build $2,900.
|
|
41
|
+
Scope: https://axelfreeman.com/scope.html · Pricing: https://axelfreeman.com/pricing.html
|
|
42
|
+
|
|
43
|
+
MIT © Axel Freeman
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* marketing-mindset CLI — the skill and its numbers without opening a file.
|
|
3
|
+
* marketing-mindset help + the test-size floors
|
|
4
|
+
* marketing-mindset sample required per-arm sample size for a two-proportion test
|
|
5
|
+
* marketing-mindset kill the kill rule for a running test
|
|
6
|
+
* marketing-mindset sections list every section of SKILL.md
|
|
7
|
+
* marketing-mindset read <text> print one section by fuzzy title match
|
|
8
|
+
* marketing-mindset install copy the skill into your agent's skills directory
|
|
9
|
+
*/
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const path = require("path");
|
|
12
|
+
|
|
13
|
+
const pkgRoot = path.join(__dirname, "..");
|
|
14
|
+
const SKILL = path.join(pkgRoot, "skill", "SKILL.md");
|
|
15
|
+
|
|
16
|
+
const ZA = { "0.05": 1.9599639845, "0.01": 2.5758293036, "0.1": 1.6448536270, "0.975": 1.9599639845 };
|
|
17
|
+
const ZB = { "0.8": 0.8416212336, "0.9": 1.2815515655 };
|
|
18
|
+
|
|
19
|
+
function sampleSize(rate, lift, power, alpha) {
|
|
20
|
+
const p1 = Math.min(Math.max(rate, 0.000001), 0.999999);
|
|
21
|
+
const p2 = Math.min(p1 * (1 + lift), 0.999999);
|
|
22
|
+
const pbar = (p1 + p2) / 2;
|
|
23
|
+
const za = ZA[String(alpha)] || ZA["0.05"];
|
|
24
|
+
const zb = ZB[String(power)] || ZB["0.8"];
|
|
25
|
+
const num = Math.pow(za * Math.sqrt(2 * pbar * (1 - pbar)) + zb * Math.sqrt(p1 * (1 - p1) + p2 * (1 - p2)), 2);
|
|
26
|
+
return Math.ceil(num / Math.pow(p2 - p1, 2));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function flags(argv) {
|
|
30
|
+
const out = {};
|
|
31
|
+
argv.forEach((a, i) => {
|
|
32
|
+
if (a.startsWith("--")) {
|
|
33
|
+
const v = argv[i + 1];
|
|
34
|
+
out[a.slice(2)] = v && !v.startsWith("--") ? Number(v) : true;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function sections(md) {
|
|
41
|
+
const lines = md.split("\n");
|
|
42
|
+
const out = [];
|
|
43
|
+
let cur = null;
|
|
44
|
+
lines.forEach((l) => {
|
|
45
|
+
const m = /^##\s+(.*)$/.exec(l);
|
|
46
|
+
if (m) {
|
|
47
|
+
if (cur) out.push(cur);
|
|
48
|
+
cur = { title: m[1].trim(), body: [] };
|
|
49
|
+
} else if (cur) cur.body.push(l);
|
|
50
|
+
});
|
|
51
|
+
if (cur) out.push(cur);
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function readSkill() {
|
|
56
|
+
if (!fs.existsSync(SKILL)) {
|
|
57
|
+
console.error("SKILL.md missing from this install (expected at " + SKILL + ")");
|
|
58
|
+
process.exit(2);
|
|
59
|
+
}
|
|
60
|
+
return fs.readFileSync(SKILL, "utf8");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const cmd = (process.argv[2] || "").toLowerCase();
|
|
64
|
+
const rest = process.argv.slice(3);
|
|
65
|
+
|
|
66
|
+
if (cmd === "sample") {
|
|
67
|
+
const f = flags(rest);
|
|
68
|
+
const rate = Number(f.base !== undefined ? f.base : 0.03);
|
|
69
|
+
const lift = Number(f.lift !== undefined ? f.lift : 0.2) ;
|
|
70
|
+
const power = Number(f.power !== undefined ? f.power : 0.8);
|
|
71
|
+
const alpha = Number(f.alpha !== undefined ? f.alpha : 0.05);
|
|
72
|
+
const n = sampleSize(rate, lift, power, alpha);
|
|
73
|
+
console.log(JSON.stringify({
|
|
74
|
+
baseline_rate: rate, min_relative_lift: lift, power, alpha,
|
|
75
|
+
per_arm: n, total: n * 2,
|
|
76
|
+
note: "Below this, a difference between variants is random noise. Declare this number before the test starts."
|
|
77
|
+
}, null, 2));
|
|
78
|
+
} else if (cmd === "kill") {
|
|
79
|
+
const f = flags(rest);
|
|
80
|
+
const rate = Number(f.base !== undefined ? f.base : 0.03);
|
|
81
|
+
const n = Number(f.n !== undefined ? f.n : 1500);
|
|
82
|
+
const lift = Number(f.lift !== undefined ? f.lift : 0.2);
|
|
83
|
+
const floor = Math.max(0, Math.floor(rate * n - 1.2816 * Math.sqrt(n * rate * (1 - rate))));
|
|
84
|
+
const needed = sampleSize(rate, lift, 0.8, 0.05);
|
|
85
|
+
console.log(JSON.stringify({
|
|
86
|
+
baseline_rate: rate, n, min_relative_lift: lift,
|
|
87
|
+
expected_conversions: Math.round(rate * n * 10) / 10,
|
|
88
|
+
kill_floor: floor,
|
|
89
|
+
target_conversions: Math.round(rate * (1 + lift) * n * 10) / 10,
|
|
90
|
+
volume_needed_per_arm: needed,
|
|
91
|
+
verdict: n < needed
|
|
92
|
+
? "keep running — below the required volume, a verdict now is noise"
|
|
93
|
+
: "read it — volume is sufficient; kill if conversions are at or under the floor"
|
|
94
|
+
}, null, 2));
|
|
95
|
+
} else if (cmd === "sections") {
|
|
96
|
+
sections(readSkill()).forEach((s, i) => console.log(String(i + 1).padStart(2) + ". " + s.title));
|
|
97
|
+
} else if (cmd === "read") {
|
|
98
|
+
const q = rest.join(" ").toLowerCase();
|
|
99
|
+
if (!q) { console.error("usage: marketing-mindset read <part of a section title>"); process.exit(1); }
|
|
100
|
+
const hit = sections(readSkill()).find((s) => s.title.toLowerCase().includes(q));
|
|
101
|
+
if (!hit) {
|
|
102
|
+
console.error("no section matches: " + q + " — run: marketing-mindset sections");
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
console.log("## " + hit.title + "\n" + hit.body.join("\n").trim());
|
|
106
|
+
} else if (cmd === "install") {
|
|
107
|
+
require(path.join(__dirname, "install.js"));
|
|
108
|
+
} else {
|
|
109
|
+
console.log(`marketing-mindset — the marketing OS for AI agents
|
|
110
|
+
|
|
111
|
+
Usage:
|
|
112
|
+
npx marketing-mindset this help + the floors
|
|
113
|
+
npx marketing-mindset sample required per-arm sample size
|
|
114
|
+
--base 0.03 --lift 0.2 --power 0.8 --alpha 0.05
|
|
115
|
+
npx marketing-mindset kill kill rule for a running test
|
|
116
|
+
--base 0.03 --n 1500 --lift 0.2
|
|
117
|
+
npx marketing-mindset sections every section of the skill
|
|
118
|
+
npx marketing-mindset read numbers print one section
|
|
119
|
+
npx marketing-mindset install copy the skill into your agent's skills dir
|
|
120
|
+
|
|
121
|
+
Floors that do not move (see SKILL.md):
|
|
122
|
+
cold email reply-rate test ~1,500-2,000 sends per variant
|
|
123
|
+
landing page smoke test 100-200 targeted visitors
|
|
124
|
+
strict A/B test ~10,000 visitors per variation
|
|
125
|
+
paid ads spend gate 1-3x target CPA, 48-72h
|
|
126
|
+
|
|
127
|
+
Docs: https://axelfreeman.github.io/marketing-mindset/
|
|
128
|
+
Skill: https://github.com/axelfreeman/marketing-mindset`);
|
|
129
|
+
}
|
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: test-size floors, kill rules, channel economics.
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "A marketer's operating mindset as an installable agent skill and CLI: test-size floors, kill rules, channel economics. npx marketing-mindset sample|kill|sections|read|install.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-skill",
|
|
7
7
|
"skill",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"url": "git+https://github.com/axelfreeman/marketing-mindset.git"
|
|
23
23
|
},
|
|
24
24
|
"bin": {
|
|
25
|
-
"marketing-mindset": "bin/
|
|
25
|
+
"marketing-mindset": "bin/cli.js",
|
|
26
|
+
"marketing-mindset-install": "bin/install.js"
|
|
26
27
|
},
|
|
27
28
|
"files": [
|
|
28
29
|
"bin/",
|