content-grade 1.0.35 → 1.0.36

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 CHANGED
@@ -51,7 +51,9 @@ content-grade activate # unlock Pro: batch mode, 100/day
51
51
 
52
52
  ## Upgrade to Pro
53
53
 
54
- **Free tier:** 3 analyses/day enough to try it, not enough to rely on it.
54
+ **The free tier is real.** 3 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
  |---|---|---|
@@ -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
- After payment you'll receive a license key by email. Activate it in one command:
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
  ---
@@ -293,15 +312,16 @@ content-grade analyze ./post.md --verbose
293
312
 
294
313
  ### Daily limit reached
295
314
 
296
- Free tier: 50 CLI analyses/day, resets at midnight UTC.
315
+ Free tier: 3 analyses/day, resets at midnight UTC.
297
316
 
298
317
  ```
299
- ✗ Daily limit reached (50/50 free checks used today).
318
+ ✗ Daily limit reached (3/3 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
- - Unlock 100/day with Pro: `content-grade activate`
324
+ - Upgrade to Pro for unlimited analyses: `content-grade activate`
305
325
 
306
326
  ---
307
327
 
@@ -119,9 +119,9 @@ const TIER_LIMITS = {
119
119
 
120
120
  function getUpgradeMessage() {
121
121
  const tier = getLicenseTier();
122
- if (tier === 'free') return `Upgrade to Pro — unlimited analyses for $9/mo → ${UPGRADE_LINKS.free}`;
123
- if (tier === 'pro') return `Upgrade to Business — 100 analyses/day for $29/mo → ${UPGRADE_LINKS.pro}`;
124
- if (tier === 'business') return `Upgrade to Team — 500 analyses/day for $79/mo → ${UPGRADE_LINKS.business}`;
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,29 +131,38 @@ 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
- // Last free run strong CTA, no escape hatch
139
- console.log(` ${RD}${B}${count}/${limit} free analyses used — limit reached.${R}`);
142
+ // Limit reachedbenefit-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. Analyze your entire content backlog,${R}`);
142
- console.log(` ${WH}run it in CI on every draft, batch-score a whole directory.${R}`);
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(` ${D}Used by 1,100+ developers. $9/mo, cancel anytime.${R}`);
149
+ console.log(` ${MG}${B}→ Upgrade ($9/mo): ${CY}${UPGRADE_LINKS.free}${R}`);
145
150
  blank();
146
- console.log(` ${MG}${B}→ Get Pro: ${CY}${UPGRADE_LINKS.free}${R}`);
147
- console.log(` ${WH} After purchase → get your key: ${CY}https://content-grade.onrender.com/my-license${R}`);
148
- console.log(` ${WH} Have a key? ${CY}content-grade activate <key>${R}`);
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 — tease Pro features
151
- console.log(` ${YL}${B}${count}/${limit} free analyses used1 left.${R}`);
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(` ${MG} Pro: unlimited analyses + CI mode + bulk grading. $9/mo: ${CY}${UPGRADE_LINKS.free}${R}`);
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 remaininglight nudge with value hook
156
- console.log(` ${WH}${count}/${limit} free analyses · ${remaining} left · Unlimited with Pro ($9/mo): ${CY}${UPGRADE_LINKS.free}${R}`);
163
+ // Runs availablebenefit-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
168
  console.log(` ${D}⭐ Like ContentGrade? Star us: https://github.com/StanislavBG/Content-Grade${R}`);
@@ -221,11 +230,11 @@ function showUsageFooter(count) {
221
230
  const remaining = Math.max(0, limit - count);
222
231
  blank();
223
232
  if (remaining === 0) {
224
- console.log(` ${RD}[ ${count}/${limit} free runs used limit reached. Unlimited runs: ${UPGRADE_LINKS.free} ]${R}`);
233
+ console.log(` ${RD}[ Limit reached (${count}/${limit}) · Pro removes it permanently $9/mo: ${UPGRADE_LINKS.free} ]${R}`);
225
234
  } else if (remaining === 1) {
226
- console.log(` ${YL}[ ${count}/${limit} free runs used · 1 left. Unlimited runs: ${UPGRADE_LINKS.free} ]${R}`);
235
+ console.log(` ${YL}[ 1 free run left today upgrade to avoid the block: ${UPGRADE_LINKS.free} ]${R}`);
227
236
  } else {
228
- console.log(` ${D}[ ${count}/${limit} free runs used · ${remaining} left. Unlimited: ${UPGRADE_LINKS.free} ]${R}`);
237
+ console.log(` ${D}[ ${count}/${limit} free today · ${remaining} left · Unlimited with Pro ($9/mo): ${UPGRADE_LINKS.free} ]${R}`);
229
238
  }
230
239
  maybeShowEarlyAdopterCTA(count);
231
240
  maybeShowFeedbackCTA(count);
@@ -259,20 +268,17 @@ function checkFreeTierLimit() {
259
268
 
260
269
  blank();
261
270
  hr();
262
- console.log(` ${YL}${B}You've used ${usage.count}/${limit} free analyses today.${R}`);
263
- blank();
264
- console.log(` ${WH}${B}Upgrade to Pro — unlimited analyses, $9/mo${R}`);
271
+ console.log(` ${YL}${B}Daily limit reached — ${usage.count}/${limit} free analyses used today.${R}`);
265
272
  blank();
266
- console.log(` ${D}Pro removes the daily cap entirely:${R}`);
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}`);
273
+ console.log(` ${MG}${B}→ Upgrade to Pro ($9/mo): ${CY}${UPGRADE_LINKS.free}${R}`);
271
274
  blank();
272
- console.log(` ${MG}${B}→ Get Pro ($9/mo): ${CY}${UPGRADE_LINKS.free}${R}`);
275
+ console.log(` ${D}After checkout you're running again in 2 minutes:${R}`);
276
+ console.log(` ${D} 1. Get your key: ${CY}https://content-grade.onrender.com/my-license${R}`);
277
+ console.log(` ${D} 2. Activate: ${CY}content-grade activate <your-key>${R}`);
278
+ console.log(` ${D} 3. Re-run your command — no cap, no counter.${R}`);
279
+ console.log(` ${D} Already have a key? Skip to step 2.${R}`);
273
280
  blank();
274
- console.log(` ${D} After purchase get your license key: ${CY}https://content-grade.onrender.com/my-license${R}`);
275
- console.log(` ${D} Already have a key? ${CY}content-grade activate <key>${R}`);
281
+ console.log(` ${D}Pro unlocks: unlimited analyses · batch mode · CI integration · JSON/HTML output${R}`);
276
282
  hr();
277
283
  blank();
278
284
  return true;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "content-grade",
3
- "version": "1.0.35",
3
+ "version": "1.0.36",
4
4
  "description": "AI-powered content analysis CLI. Score any blog post, landing page, or ad copy in under 30 seconds — runs on Claude CLI, no API key needed.",
5
5
  "type": "module",
6
6
  "bin": {