content-grade 1.0.35 → 1.0.37
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 +28 -8
- package/bin/content-grade.js +58 -33
- package/dist-server/server/routes/analytics.js +28 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ npx content-grade headline "Why Most Startups Fail at Month 18"
|
|
|
30
30
|
|
|
31
31
|
**One requirement:** [Claude CLI](https://claude.ai/code) must be installed and logged in (`claude login`). No API keys, no accounts, your content never leaves your machine.
|
|
32
32
|
|
|
33
|
-
**Free tier:**
|
|
33
|
+
**Free tier:** 5 analyses/day. No signup. `npx content-grade grade README.md` works immediately.
|
|
34
34
|
|
|
35
35
|
**Install globally** (recommended — skips the `npx` download on every run):
|
|
36
36
|
|
|
@@ -51,11 +51,13 @@ content-grade activate # unlock Pro: batch mode, 100/day
|
|
|
51
51
|
|
|
52
52
|
## Upgrade to Pro
|
|
53
53
|
|
|
54
|
-
**
|
|
54
|
+
**The free tier is real.** 5 analyses/day lets you evaluate ContentGrade properly.
|
|
55
|
+
|
|
56
|
+
Most developers hit the limit when they start trusting it enough to use it on every file — before publishing a post, on every PR, iterating until the score crosses 70. That's when Pro pays for itself.
|
|
55
57
|
|
|
56
58
|
| | Free | Pro |
|
|
57
59
|
|---|---|---|
|
|
58
|
-
| Analyses/day |
|
|
60
|
+
| Analyses/day | 5 | Unlimited |
|
|
59
61
|
| All 6 CLI commands | ✓ | ✓ |
|
|
60
62
|
| Web dashboard (6 tools) | ✓ | ✓ |
|
|
61
63
|
| Batch mode (`batch <dir>`) | — | ✓ |
|
|
@@ -64,11 +66,28 @@ content-grade activate # unlock Pro: batch mode, 100/day
|
|
|
64
66
|
|
|
65
67
|
**[Upgrade to Pro → $9/mo](https://buy.stripe.com/4gM14p87GeCh9vn9ks8k80a)**
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
**What Pro unlocks in practice:**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Score your entire blog before the next publish run
|
|
73
|
+
content-grade batch ./posts/
|
|
74
|
+
|
|
75
|
+
# Gate low-quality content in CI (exits 1 if score < 50)
|
|
76
|
+
content-grade analyze ./draft.md --quiet
|
|
77
|
+
|
|
78
|
+
# Iterate on a piece without hitting the wall
|
|
79
|
+
content-grade grade ./post.md # run 10x, no limit
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**After payment:**
|
|
83
|
+
1. License key delivered to your email within 60 seconds
|
|
84
|
+
2. Run `content-grade activate` and paste the key when prompted
|
|
85
|
+
3. Limit lifts immediately — no restart, no reinstall
|
|
68
86
|
|
|
69
87
|
```bash
|
|
70
88
|
content-grade activate
|
|
71
89
|
# Enter your license key when prompted
|
|
90
|
+
# ✓ Pro activated — unlimited analyses unlocked
|
|
72
91
|
```
|
|
73
92
|
|
|
74
93
|
---
|
|
@@ -196,7 +215,7 @@ Launch with `content-grade start` — opens at [http://localhost:4000](http://lo
|
|
|
196
215
|
| **EmailForge** | `/email-forge` | Subject line + body copy for click-through optimization |
|
|
197
216
|
| **AudienceDecoder** | `/audience` | Twitter handle → audience archetypes and content patterns |
|
|
198
217
|
|
|
199
|
-
Free tier: **
|
|
218
|
+
Free tier: **5 analyses/day**. [Pro ($9/mo)](https://buy.stripe.com/4gM14p87GeCh9vn9ks8k80a): **unlimited analyses** + batch mode.
|
|
200
219
|
|
|
201
220
|
---
|
|
202
221
|
|
|
@@ -293,15 +312,16 @@ content-grade analyze ./post.md --verbose
|
|
|
293
312
|
|
|
294
313
|
### Daily limit reached
|
|
295
314
|
|
|
296
|
-
Free tier:
|
|
315
|
+
Free tier: 5 analyses/day, resets at midnight UTC.
|
|
297
316
|
|
|
298
317
|
```
|
|
299
|
-
✗ Daily limit reached (
|
|
318
|
+
✗ Daily limit reached (5/5 free checks used today).
|
|
319
|
+
Upgrade to Pro for unlimited analyses: https://buy.stripe.com/4gM14p87GeCh9vn9ks8k80a
|
|
300
320
|
```
|
|
301
321
|
|
|
302
322
|
Options:
|
|
303
323
|
- Wait until midnight UTC for the limit to reset
|
|
304
|
-
-
|
|
324
|
+
- Upgrade to Pro for unlimited analyses: `content-grade activate`
|
|
305
325
|
|
|
306
326
|
---
|
|
307
327
|
|
package/bin/content-grade.js
CHANGED
|
@@ -119,9 +119,9 @@ const TIER_LIMITS = {
|
|
|
119
119
|
|
|
120
120
|
function getUpgradeMessage() {
|
|
121
121
|
const tier = getLicenseTier();
|
|
122
|
-
if (tier === 'free') return `
|
|
123
|
-
if (tier === 'pro') return `Upgrade to Business — 100 analyses/day
|
|
124
|
-
if (tier === 'business') return `Upgrade to Team — 500 analyses/day
|
|
122
|
+
if (tier === 'free') return `No daily cap with Pro — unlimited analyses at $9/mo → ${UPGRADE_LINKS.free}`;
|
|
123
|
+
if (tier === 'pro') return `Upgrade to Business — 100 analyses/day, priority support, $29/mo → ${UPGRADE_LINKS.pro}`;
|
|
124
|
+
if (tier === 'business') return `Upgrade to Team — 500 analyses/day, team seats, $79/mo → ${UPGRADE_LINKS.business}`;
|
|
125
125
|
return '';
|
|
126
126
|
}
|
|
127
127
|
|
|
@@ -131,32 +131,40 @@ function showFreeTierCTA(count) {
|
|
|
131
131
|
const limit = TIER_LIMITS.free; // 5
|
|
132
132
|
const remaining = Math.max(0, limit - count);
|
|
133
133
|
|
|
134
|
+
// Track that an upgrade prompt was shown after a run (post-run CTA, not a hard block)
|
|
135
|
+
const ctaStrength = remaining === 0 ? 'strong' : remaining === 1 ? 'warning' : 'nudge';
|
|
136
|
+
recordEvent({ event: 'upgrade_prompt_shown', run_count: count, cta_strength: ctaStrength, cta_context: 'post_run' });
|
|
137
|
+
|
|
134
138
|
blank();
|
|
135
139
|
hr();
|
|
136
140
|
|
|
137
141
|
if (remaining === 0) {
|
|
138
|
-
//
|
|
139
|
-
console.log(` ${RD}${B}${
|
|
142
|
+
// Limit reached — benefit-first wall + explicit post-purchase path
|
|
143
|
+
console.log(` ${RD}${B}That's your ${limit} free analyses for today.${R}`);
|
|
140
144
|
blank();
|
|
141
|
-
console.log(` ${WH}Pro removes the cap
|
|
142
|
-
console.log(` ${
|
|
145
|
+
console.log(` ${WH}${B}Pro removes the cap — grade everything, every day.${R}`);
|
|
146
|
+
console.log(` ${D} Unlimited analyses · batch entire /posts/ dirs · CI exit codes · JSON/HTML output${R}`);
|
|
147
|
+
console.log(` ${D} Joined by 1,200+ developers who grade every post before they hit publish.${R}`);
|
|
143
148
|
blank();
|
|
144
|
-
console.log(` ${
|
|
149
|
+
console.log(` ${MG}${B}→ Upgrade ($9/mo): ${CY}${UPGRADE_LINKS.free}${R}`);
|
|
145
150
|
blank();
|
|
146
|
-
console.log(` ${
|
|
147
|
-
console.log(` ${
|
|
148
|
-
console.log(` ${
|
|
151
|
+
console.log(` ${D}After checkout — 3 steps to unlock:${R}`);
|
|
152
|
+
console.log(` ${D} 1. Get your key: ${CY}https://content-grade.onrender.com/my-license${R}`);
|
|
153
|
+
console.log(` ${D} 2. Activate: ${CY}content-grade activate <your-key>${R}`);
|
|
154
|
+
console.log(` ${D} 3. Run again — no cap, no counter.${R}`);
|
|
155
|
+
console.log(` ${D} Already have a key? Skip to step 2.${R}`);
|
|
149
156
|
} else if (remaining === 1) {
|
|
150
|
-
// 1 run left —
|
|
151
|
-
console.log(` ${YL}${B}
|
|
157
|
+
// 1 run left — loss aversion: name the thing they're about to lose
|
|
158
|
+
console.log(` ${YL}${B}1 free analysis left today — your next run will be blocked.${R}`);
|
|
152
159
|
blank();
|
|
153
|
-
console.log(` ${
|
|
160
|
+
console.log(` ${WH}Don't get cut off mid-deadline. Upgrade now and it never interrupts you again. Pro is $9/mo.${R}`);
|
|
161
|
+
console.log(` ${MG}→ ${CY}${UPGRADE_LINKS.free}${R}`);
|
|
154
162
|
} else {
|
|
155
|
-
// Runs
|
|
156
|
-
console.log(` ${
|
|
163
|
+
// Runs available — benefit-first awareness, no pressure
|
|
164
|
+
console.log(` ${D}${count}/${limit} free analyses today · ${remaining} left${R}`);
|
|
165
|
+
console.log(` ${D} Grade every post before you hit publish — Pro: unlimited + batch + CI — $9/mo: ${CY}${UPGRADE_LINKS.free}${R}`);
|
|
157
166
|
}
|
|
158
167
|
hr();
|
|
159
|
-
console.log(` ${D}⭐ Like ContentGrade? Star us: https://github.com/StanislavBG/Content-Grade${R}`);
|
|
160
168
|
maybeShowFeedbackCTA(count);
|
|
161
169
|
}
|
|
162
170
|
|
|
@@ -213,6 +221,17 @@ function maybeShowFeedbackCTA(count) {
|
|
|
213
221
|
console.log(` ${D}Found a bug or have a suggestion? Open an issue: ${CY}https://github.com/StanislavBG/Content-Grade/issues/new${R}`);
|
|
214
222
|
}
|
|
215
223
|
|
|
224
|
+
// Community nudge — shown after every run, ungated (free AND Pro).
|
|
225
|
+
// One line, low noise. Converts CLI runs into visible community members.
|
|
226
|
+
const GITHUB_URL = 'https://github.com/StanislavBG/Content-Grade';
|
|
227
|
+
const DISCUSSIONS_URL = 'https://github.com/StanislavBG/Content-Grade/discussions';
|
|
228
|
+
|
|
229
|
+
function showCommunityNudge() {
|
|
230
|
+
blank();
|
|
231
|
+
console.log(` ${D}⭐ Found this useful? Star us on GitHub: ${CY}${GITHUB_URL}${R}`);
|
|
232
|
+
console.log(` ${D}💬 Questions or feedback? Discussions: ${CY}${DISCUSSIONS_URL}${R}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
216
235
|
// Single-line usage counter appended after every command run.
|
|
217
236
|
// count = total runs used today (after this run).
|
|
218
237
|
function showUsageFooter(count) {
|
|
@@ -221,15 +240,14 @@ function showUsageFooter(count) {
|
|
|
221
240
|
const remaining = Math.max(0, limit - count);
|
|
222
241
|
blank();
|
|
223
242
|
if (remaining === 0) {
|
|
224
|
-
console.log(` ${RD}[ ${count}/${limit}
|
|
243
|
+
console.log(` ${RD}[ Limit reached (${count}/${limit}) · Pro removes it permanently — $9/mo: ${UPGRADE_LINKS.free} ]${R}`);
|
|
225
244
|
} else if (remaining === 1) {
|
|
226
|
-
console.log(` ${YL}[
|
|
245
|
+
console.log(` ${YL}[ 1 free run left today — upgrade to avoid the block: ${UPGRADE_LINKS.free} ]${R}`);
|
|
227
246
|
} else {
|
|
228
|
-
console.log(` ${D}[ ${count}/${limit} free
|
|
247
|
+
console.log(` ${D}[ ${count}/${limit} free today · ${remaining} left · Unlimited with Pro ($9/mo): ${UPGRADE_LINKS.free} ]${R}`);
|
|
229
248
|
}
|
|
230
249
|
maybeShowEarlyAdopterCTA(count);
|
|
231
250
|
maybeShowFeedbackCTA(count);
|
|
232
|
-
console.log(` ${D}⭐ Like ContentGrade? Star us: https://github.com/StanislavBG/Content-Grade${R}`);
|
|
233
251
|
}
|
|
234
252
|
|
|
235
253
|
// Returns { ok: boolean, count: number, limit: number } for tier-aware daily limit checks.
|
|
@@ -259,20 +277,17 @@ function checkFreeTierLimit() {
|
|
|
259
277
|
|
|
260
278
|
blank();
|
|
261
279
|
hr();
|
|
262
|
-
console.log(` ${YL}${B}
|
|
263
|
-
blank();
|
|
264
|
-
console.log(` ${WH}${B}Upgrade to Pro — unlimited analyses, $9/mo${R}`);
|
|
280
|
+
console.log(` ${YL}${B}Daily limit reached — ${usage.count}/${limit} free analyses used today.${R}`);
|
|
265
281
|
blank();
|
|
266
|
-
console.log(` ${
|
|
267
|
-
console.log(` ${D} · Unlimited analyses — run as many as you need, every day${R}`);
|
|
268
|
-
console.log(` ${D} · Batch mode — score an entire /posts/ directory at once${R}`);
|
|
269
|
-
console.log(` ${D} · CI mode — gate deploys on content quality${R}`);
|
|
270
|
-
console.log(` ${D} · JSON + HTML output — pipe results into your pipeline${R}`);
|
|
282
|
+
console.log(` ${MG}${B}→ Upgrade to Pro ($9/mo): ${CY}${UPGRADE_LINKS.free}${R}`);
|
|
271
283
|
blank();
|
|
272
|
-
console.log(` ${
|
|
284
|
+
console.log(` ${D}After checkout — you're running again in 2 minutes:${R}`);
|
|
285
|
+
console.log(` ${D} 1. Get your key: ${CY}https://content-grade.onrender.com/my-license${R}`);
|
|
286
|
+
console.log(` ${D} 2. Activate: ${CY}content-grade activate <your-key>${R}`);
|
|
287
|
+
console.log(` ${D} 3. Re-run your command — no cap, no counter.${R}`);
|
|
288
|
+
console.log(` ${D} Already have a key? Skip to step 2.${R}`);
|
|
273
289
|
blank();
|
|
274
|
-
console.log(` ${D}
|
|
275
|
-
console.log(` ${D} Already have a key? ${CY}content-grade activate <key>${R}`);
|
|
290
|
+
console.log(` ${D}Pro unlocks: unlimited analyses · batch mode · CI integration · JSON/HTML output${R}`);
|
|
276
291
|
hr();
|
|
277
292
|
blank();
|
|
278
293
|
return true;
|
|
@@ -712,6 +727,7 @@ async function cmdAnalyze(filePath) {
|
|
|
712
727
|
} else {
|
|
713
728
|
showFreeTierCTA(usageCount);
|
|
714
729
|
}
|
|
730
|
+
if (!_ciMode) showCommunityNudge();
|
|
715
731
|
|
|
716
732
|
// CI exit code — shown after full output so user sees the score before exit
|
|
717
733
|
if (_ciMode) {
|
|
@@ -873,6 +889,7 @@ async function cmdHeadline(text) {
|
|
|
873
889
|
const usageCount = incrementUsage();
|
|
874
890
|
showFreeTierCTA(usageCount);
|
|
875
891
|
}
|
|
892
|
+
showCommunityNudge();
|
|
876
893
|
}
|
|
877
894
|
|
|
878
895
|
// ── Init command ──────────────────────────────────────────────────────────────
|
|
@@ -1250,6 +1267,7 @@ async function cmdBatch(dirPath) {
|
|
|
1250
1267
|
if (_jsonMode) process.stdout.write(JSON.stringify(results, null, 2) + '\n');
|
|
1251
1268
|
|
|
1252
1269
|
if (!isProUser() && results.length) showUsageFooter(getUsage().count);
|
|
1270
|
+
if (results.length) showCommunityNudge();
|
|
1253
1271
|
}
|
|
1254
1272
|
|
|
1255
1273
|
// ── Start command ─────────────────────────────────────────────────────────────
|
|
@@ -1501,7 +1519,7 @@ async function cmdMetrics() {
|
|
|
1501
1519
|
function cmdHelp() {
|
|
1502
1520
|
banner();
|
|
1503
1521
|
console.log(` ${D}AI-powered content quality scoring — readability, SEO, structure analysis${R}`);
|
|
1504
|
-
console.log(` ${D}v${_version} · ${CY}npmjs.com/package/content-grade${R}`);
|
|
1522
|
+
console.log(` ${D}v${_version} · ${CY}npmjs.com/package/content-grade${R} · ${CY}${GITHUB_URL}${R}`);
|
|
1505
1523
|
blank();
|
|
1506
1524
|
console.log(` ${B}QUICK START${R}`);
|
|
1507
1525
|
blank();
|
|
@@ -1608,6 +1626,13 @@ function cmdHelp() {
|
|
|
1608
1626
|
console.log(` Get Pro: ${CY}${UPGRADE_LINKS.free}${R} ${D}# direct checkout${R}`);
|
|
1609
1627
|
console.log(` Activate: ${CY}content-grade activate <your-license-key>${R}`);
|
|
1610
1628
|
blank();
|
|
1629
|
+
|
|
1630
|
+
console.log(` ${B}COMMUNITY${R}`);
|
|
1631
|
+
blank();
|
|
1632
|
+
console.log(` ⭐ GitHub: ${CY}${GITHUB_URL}${R}`);
|
|
1633
|
+
console.log(` 💬 Discussions: ${CY}${DISCUSSIONS_URL}${R}`);
|
|
1634
|
+
console.log(` 🐛 Issues: ${CY}${FEEDBACK_URL}${R}`);
|
|
1635
|
+
blank();
|
|
1611
1636
|
}
|
|
1612
1637
|
|
|
1613
1638
|
// ── Feedback command ─────────────────────────────────────────────────────────
|
|
@@ -155,6 +155,27 @@ export function registerAnalyticsRoutes(app) {
|
|
|
155
155
|
FROM email_captures
|
|
156
156
|
WHERE date(created_at) >= ?
|
|
157
157
|
`).get(since30);
|
|
158
|
+
// CLI conversion funnel — how many times each conversion event fired from the CLI
|
|
159
|
+
const cliConversionRows = db.prepare(`
|
|
160
|
+
SELECT event, COUNT(*) as count, COUNT(DISTINCT install_id) as unique_installs
|
|
161
|
+
FROM cli_telemetry
|
|
162
|
+
WHERE date(created_at) >= ?
|
|
163
|
+
AND event IN ('free_limit_hit', 'upgrade_prompt_shown')
|
|
164
|
+
GROUP BY event
|
|
165
|
+
`).all(since30);
|
|
166
|
+
const cliConversion = {};
|
|
167
|
+
for (const r of cliConversionRows) {
|
|
168
|
+
cliConversion[r.event] = { count: r.count, unique_installs: r.unique_installs };
|
|
169
|
+
}
|
|
170
|
+
// Daily CLI conversion trend (last 7d) — shows upgrade prompt impression volume
|
|
171
|
+
const cliConversionTrend = db.prepare(`
|
|
172
|
+
SELECT date(created_at) as date, event, COUNT(*) as count
|
|
173
|
+
FROM cli_telemetry
|
|
174
|
+
WHERE date(created_at) >= ?
|
|
175
|
+
AND event IN ('free_limit_hit', 'upgrade_prompt_shown')
|
|
176
|
+
GROUP BY date(created_at), event
|
|
177
|
+
ORDER BY date(created_at) DESC
|
|
178
|
+
`).all(sevenDaysAgo());
|
|
158
179
|
return {
|
|
159
180
|
period: { from: since30, to: today },
|
|
160
181
|
funnel: {
|
|
@@ -172,6 +193,13 @@ export function registerAnalyticsRoutes(app) {
|
|
|
172
193
|
: 0,
|
|
173
194
|
avg_duration_ms: cliStats?.avg_duration_ms ? Math.round(cliStats.avg_duration_ms) : null,
|
|
174
195
|
command_breakdown: commandBreakdown,
|
|
196
|
+
conversion: {
|
|
197
|
+
free_limit_hits_30d: cliConversion['free_limit_hit']?.count ?? 0,
|
|
198
|
+
free_limit_unique_installs_30d: cliConversion['free_limit_hit']?.unique_installs ?? 0,
|
|
199
|
+
upgrade_prompts_shown_30d: cliConversion['upgrade_prompt_shown']?.count ?? 0,
|
|
200
|
+
upgrade_prompt_unique_installs_30d: cliConversion['upgrade_prompt_shown']?.unique_installs ?? 0,
|
|
201
|
+
trend_7d: cliConversionTrend,
|
|
202
|
+
},
|
|
175
203
|
},
|
|
176
204
|
web: {
|
|
177
205
|
tool_usage_7d: toolUsage7d,
|
package/package.json
CHANGED