content-grade 1.0.37 → 1.0.38

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
@@ -229,6 +229,7 @@ Free tier: **5 analyses/day**. [Pro ($9/mo)](https://buy.stripe.com/4gM14p87GeCh
229
229
  | [Show & Tell](https://github.com/StanislavBG/Content-Grade/discussions/new?category=show-and-tell) | Workflows, CI integrations, results — claim Early Adopter here |
230
230
  | [Ideas](https://github.com/StanislavBG/Content-Grade/discussions/new?category=ideas) | Feature requests before they become issues |
231
231
  | [Bug reports](https://github.com/StanislavBG/Content-Grade/issues/new?template=bug_report.yml) | Something broken? File it here |
232
+ | [Feedback](https://github.com/StanislavBG/Content-Grade/issues/new?title=Feedback%3A+&labels=feedback) | General feedback — what's working, what isn't |
232
233
 
233
234
  **[All discussions →](https://github.com/StanislavBG/Content-Grade/discussions)**
234
235
 
@@ -236,6 +237,18 @@ Free tier: **5 analyses/day**. [Pro ($9/mo)](https://buy.stripe.com/4gM14p87GeCh
236
237
 
237
238
  ---
238
239
 
240
+ ## Built with Content-Grade?
241
+
242
+ Using ContentGrade in a real workflow? [Open an issue](https://github.com/StanislavBG/Content-Grade/issues/new?title=Built+with+Content-Grade%3A+&labels=use-case) or post in [Show & Tell](https://github.com/StanislavBG/Content-Grade/discussions/new?category=show-and-tell) and tell us:
243
+
244
+ - What are you grading? (blog posts, landing pages, ad copy, emails?)
245
+ - Are you running it in CI? What score threshold do you use?
246
+ - Any workflow, config, or integration worth sharing?
247
+
248
+ Use cases that get shared here get featured in this README. Posting also qualifies you for the [Early Adopter program](EARLY_ADOPTERS.md) — 12 months Pro free.
249
+
250
+ ---
251
+
239
252
  ## Requirements
240
253
 
241
254
  - **Node.js 18+**
@@ -78,22 +78,21 @@ function getLicenseTier() {
78
78
 
79
79
  function isProUser() { return Boolean(getLicenseKey()); }
80
80
 
81
- function getTodayKey() { return new Date().toISOString().slice(0, 10); }
82
-
83
81
  function getUsage() {
84
82
  try {
85
83
  const d = JSON.parse(readFileSync(USAGE_FILE, 'utf8'));
86
- if (d.date !== getTodayKey()) return { date: getTodayKey(), count: 0 };
87
- return d;
88
- } catch { return { date: getTodayKey(), count: 0 }; }
84
+ // Migrate legacy daily-reset format: if file has a date key, treat existing count as lifetime
85
+ if (d.date) return { lifetime: d.count || 0 };
86
+ return { lifetime: d.lifetime || 0 };
87
+ } catch { return { lifetime: 0 }; }
89
88
  }
90
89
 
91
90
  function incrementUsage() {
92
91
  mkdirSync(CONFIG_DIR, { recursive: true });
93
92
  const u = getUsage();
94
- u.count = (u.count || 0) + 1;
93
+ u.lifetime = (u.lifetime || 0) + 1;
95
94
  writeFileSync(USAGE_FILE, JSON.stringify(u, null, 2), 'utf8');
96
- return u.count;
95
+ return u.lifetime;
97
96
  }
98
97
 
99
98
  // Upgrade links — Free → Pro → Business → Team
@@ -111,28 +110,28 @@ const TIER_NAMES = {
111
110
  };
112
111
 
113
112
  const TIER_LIMITS = {
114
- free: 5,
113
+ free: 15,
115
114
  pro: Infinity,
116
- business: 100,
117
- team: 500,
115
+ business: Infinity,
116
+ team: Infinity,
118
117
  };
119
118
 
120
119
  function getUpgradeMessage() {
121
120
  const tier = getLicenseTier();
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}`;
121
+ if (tier === 'free') return `Unlimited analyses with Pro — $9/mo → ${UPGRADE_LINKS.free}`;
122
+ if (tier === 'pro') return `Upgrade to Business — priority support, $29/mo → ${UPGRADE_LINKS.pro}`;
123
+ if (tier === 'business') return `Upgrade to Team — team seats, $79/mo → ${UPGRADE_LINKS.business}`;
125
124
  return '';
126
125
  }
127
126
 
128
127
  // Show usage-aware upgrade CTA after each free run.
129
- // count = today's usage AFTER this run (1 = first run, 3 = last free run).
128
+ // count = lifetime usage AFTER this run.
130
129
  function showFreeTierCTA(count) {
131
- const limit = TIER_LIMITS.free; // 5
130
+ const limit = TIER_LIMITS.free; // 15
132
131
  const remaining = Math.max(0, limit - count);
133
132
 
134
133
  // 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';
134
+ const ctaStrength = remaining === 0 ? 'strong' : remaining <= 2 ? 'warning' : 'nudge';
136
135
  recordEvent({ event: 'upgrade_prompt_shown', run_count: count, cta_strength: ctaStrength, cta_context: 'post_run' });
137
136
 
138
137
  blank();
@@ -140,11 +139,10 @@ function showFreeTierCTA(count) {
140
139
 
141
140
  if (remaining === 0) {
142
141
  // Limit reached — benefit-first wall + explicit post-purchase path
143
- console.log(` ${RD}${B}That's your ${limit} free analyses for today.${R}`);
142
+ console.log(` ${RD}${B}You've used all ${limit} free analyses.${R}`);
144
143
  blank();
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}`);
144
+ console.log(` ${WH}${B}Pro removes the cap — unlimited analyses, forever.${R}`);
145
+ console.log(` ${D} Unlimited runs · batch entire /posts/ dirs · priority support · CI exit codes${R}`);
148
146
  blank();
149
147
  console.log(` ${MG}${B}→ Upgrade ($9/mo): ${CY}${UPGRADE_LINKS.free}${R}`);
150
148
  blank();
@@ -153,16 +151,17 @@ function showFreeTierCTA(count) {
153
151
  console.log(` ${D} 2. Activate: ${CY}content-grade activate <your-key>${R}`);
154
152
  console.log(` ${D} 3. Run again — no cap, no counter.${R}`);
155
153
  console.log(` ${D} Already have a key? Skip to step 2.${R}`);
156
- } else if (remaining === 1) {
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}`);
154
+ } else if (remaining <= 2) {
155
+ // 1-2 runs left — loss aversion + clear Pro value
156
+ console.log(` ${YL}${B}${count}/${limit} runs used · ${remaining} free analysis${remaining === 1 ? '' : 'es'} remaining${R}`);
159
157
  blank();
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}`);
158
+ console.log(` ${WH}${B}Pro: unlimited runs · batch analysis · priority support${R}`);
159
+ console.log(` ${MG}${B}Get Pro ($9/mo): ${CY}${UPGRADE_LINKS.free}${R}`);
162
160
  } else {
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}`);
161
+ // Runs available — always visible count + clear Pro benefits + Stripe link
162
+ console.log(` ${WH}${count}/${limit} runs used · ${remaining} remaining${R}`);
163
+ console.log(` ${D} Pro: unlimited runs · batch analysis · priority support${R}`);
164
+ console.log(` ${MG}→ Upgrade ($9/mo): ${CY}${UPGRADE_LINKS.free}${R}`);
166
165
  }
167
166
  hr();
168
167
  maybeShowFeedbackCTA(count);
@@ -185,7 +184,7 @@ function markEarlyAdopterCTAShown() {
185
184
  // Show once, after the first successful run, to non-Pro users who haven't seen it.
186
185
  function maybeShowEarlyAdopterCTA(count) {
187
186
  if (isProUser()) return;
188
- if (count !== 1) return; // only after the very first run today
187
+ if (count !== 1) return; // only after the very first run
189
188
  if (hasShownEarlyAdopterCTA()) return; // only once per install
190
189
  markEarlyAdopterCTAShown();
191
190
  blank();
@@ -233,32 +232,32 @@ function showCommunityNudge() {
233
232
  }
234
233
 
235
234
  // Single-line usage counter appended after every command run.
236
- // count = total runs used today (after this run).
235
+ // count = total lifetime runs used (after this run).
237
236
  function showUsageFooter(count) {
238
237
  if (isProUser()) return;
239
238
  const limit = TIER_LIMITS.free;
240
239
  const remaining = Math.max(0, limit - count);
241
240
  blank();
242
241
  if (remaining === 0) {
243
- console.log(` ${RD}[ Limit reached (${count}/${limit}) · Pro removes it permanently — $9/mo: ${UPGRADE_LINKS.free} ]${R}`);
244
- } else if (remaining === 1) {
245
- console.log(` ${YL}[ 1 free run left today upgrade to avoid the block: ${UPGRADE_LINKS.free} ]${R}`);
242
+ console.log(` ${RD}[ All ${limit} free analyses used · Pro removes the cap — $9/mo: ${UPGRADE_LINKS.free} ]${R}`);
243
+ } else if (remaining <= 2) {
244
+ console.log(` ${YL}[ ${remaining} free analysis${remaining === 1 ? '' : 'es'} remaining · Upgrade to Pro: ${UPGRADE_LINKS.free} ]${R}`);
246
245
  } else {
247
- console.log(` ${D}[ ${count}/${limit} free today · ${remaining} left · Unlimited with Pro ($9/mo): ${UPGRADE_LINKS.free} ]${R}`);
246
+ console.log(` ${D}[ ${count}/${limit} free analyses used · ${remaining} remaining · Unlimited with Pro ($9/mo): ${UPGRADE_LINKS.free} ]${R}`);
248
247
  }
249
248
  maybeShowEarlyAdopterCTA(count);
250
249
  maybeShowFeedbackCTA(count);
251
250
  }
252
251
 
253
- // Returns { ok: boolean, count: number, limit: number } for tier-aware daily limit checks.
254
- // Used by batch mode to stop processing when the daily limit is reached.
252
+ // Returns { ok: boolean, count: number, limit: number } for tier-aware lifetime limit checks.
253
+ // Used by batch mode to stop processing when the lifetime limit is reached.
255
254
  // Pro tier has Infinity limit and always returns ok: true.
256
- function checkDailyLimit() {
255
+ function checkLifetimeLimit() {
257
256
  const tier = getLicenseTier();
258
257
  const limit = TIER_LIMITS[tier] ?? TIER_LIMITS.free;
259
258
  if (limit === Infinity) return { ok: true, count: 0, limit: Infinity };
260
259
  const usage = getUsage();
261
- const count = usage.count || 0;
260
+ const count = usage.lifetime || 0;
262
261
  return { ok: count < limit, count, limit };
263
262
  }
264
263
 
@@ -269,25 +268,28 @@ function checkFreeTierLimit() {
269
268
  if (isProUser()) return false;
270
269
  const usage = getUsage();
271
270
  const limit = TIER_LIMITS.free;
272
- if (usage.count < limit) return false;
271
+ if (usage.lifetime < limit) return false;
273
272
 
274
273
  // Track funnel event — user hit the limit and saw the upgrade prompt
275
274
  recordEvent({ event: 'free_limit_hit', version: _version });
276
- recordEvent({ event: 'upgrade_prompt_shown', run_count: usage.count });
275
+ recordEvent({ event: 'upgrade_prompt_shown', run_count: usage.lifetime });
277
276
 
278
277
  blank();
279
278
  hr();
280
- console.log(` ${YL}${B}Daily limit reached ${usage.count}/${limit} free analyses used today.${R}`);
279
+ console.log(` ${WH}${B}You've used all ${limit} free runs Pro gives you unlimited.${R}`);
280
+ blank();
281
+ console.log(` ${WH}Pro unlocks:${R}`);
282
+ console.log(` ${D} · Unlimited runs — no daily cap, no lifetime cap${R}`);
283
+ console.log(` ${D} · Batch analysis — grade entire /posts/ directories at once${R}`);
284
+ console.log(` ${D} · Priority support — direct response within 24h${R}`);
285
+ console.log(` ${D} · CI integration — exit codes, JSON/HTML output${R}`);
281
286
  blank();
282
- console.log(` ${MG}${B}→ Upgrade to Pro ($9/mo): ${CY}${UPGRADE_LINKS.free}${R}`);
287
+ console.log(` ${MG}${B}→ Get Pro ($9/mo): ${CY}${UPGRADE_LINKS.free}${R}`);
283
288
  blank();
284
- console.log(` ${D}After checkout — you're running again in 2 minutes:${R}`);
289
+ console.log(` ${D}After checkout — running again in 2 minutes:${R}`);
285
290
  console.log(` ${D} 1. Get your key: ${CY}https://content-grade.onrender.com/my-license${R}`);
286
291
  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}`);
289
- blank();
290
- console.log(` ${D}Pro unlocks: unlimited analyses · batch mode · CI integration · JSON/HTML output${R}`);
292
+ console.log(` ${D} 3. Re-run — no cap, no counter.${R}`);
291
293
  hr();
292
294
  blank();
293
295
  return true;
@@ -988,12 +990,12 @@ async function cmdActivate() {
988
990
  blank();
989
991
  console.log(` ${B}Pro features:${R}`);
990
992
  console.log(` ${D} content-grade batch ./posts/ ${R}${D}# analyze all files in a directory${R}`);
991
- console.log(` ${D} Unlimited analyses/day (vs 5 free)${R}`);
993
+ console.log(` ${D} Unlimited analyses (vs 15 free total)${R}`);
992
994
  blank();
993
995
  return;
994
996
  }
995
997
 
996
- console.log(` ${D}Pro unlocks unlimited runs/day + batch mode for $9/mo:${R}`);
998
+ console.log(` ${D}Pro unlocks unlimited analyses + batch mode for $9/mo:${R}`);
997
999
  blank();
998
1000
  console.log(` ${MG}${B}→ Get Pro: ${CY}${UPGRADE_LINKS.free}${R}`);
999
1001
  blank();
@@ -1115,7 +1117,7 @@ async function cmdActivate() {
1115
1117
  const tierLimit = TIER_LIMITS[activatedTier] || TIER_LIMITS.free;
1116
1118
  const tierLimitDisplay = tierLimit === Infinity ? 'unlimited' : `${tierLimit}`;
1117
1119
  blank();
1118
- ok(`${(TIER_NAMES[activatedTier] || activatedTier).split(' —')[0]} activated! Your daily limit is now ${tierLimitDisplay} analyses.`);
1120
+ ok(`${(TIER_NAMES[activatedTier] || activatedTier).split(' —')[0]} activated! You now have ${tierLimitDisplay} analyses.`);
1119
1121
  blank();
1120
1122
  console.log(` ${B}Try it:${R}`);
1121
1123
  console.log(` ${CY} content-grade batch ./posts/${R} ${D}# analyze all files${R}`);
@@ -1131,7 +1133,7 @@ async function cmdBatch(dirPath) {
1131
1133
  blank();
1132
1134
  console.log(` ${D}Batch mode grades every file in a directory in one shot — Pro only.${R}`);
1133
1135
  blank();
1134
- console.log(` Pro is $9/mo and also unlocks unlimited daily runs.`);
1136
+ console.log(` Pro is $9/mo and unlocks unlimited analyses.`);
1135
1137
  blank();
1136
1138
  console.log(` ${MG}${B}→ Get Pro: ${CY}${UPGRADE_LINKS.free}${R}`);
1137
1139
  blank();
@@ -1212,12 +1214,12 @@ async function cmdBatch(dirPath) {
1212
1214
  const rel = f.startsWith(process.cwd()) ? f.slice(process.cwd().length + 1) : f;
1213
1215
  process.stdout.write(` ${D}[${i + 1}/${files.length}]${R} ${rel}...`);
1214
1216
 
1215
- // Guard: check daily limit before each analysis
1216
- const batchLimitCheck = checkDailyLimit();
1217
+ // Guard: check lifetime limit before each analysis
1218
+ const batchLimitCheck = checkLifetimeLimit();
1217
1219
  if (!batchLimitCheck.ok) {
1218
1220
  process.stdout.write(` ${YL}limit reached${R}\n`);
1219
1221
  blank();
1220
- warn(`Daily limit reached (${batchLimitCheck.count}/${batchLimitCheck.limit}). Remaining files skipped.`);
1222
+ warn(`Free limit reached (${batchLimitCheck.count}/${batchLimitCheck.limit} lifetime analyses used). Remaining files skipped.`);
1221
1223
  break;
1222
1224
  }
1223
1225
 
@@ -1266,7 +1268,7 @@ async function cmdBatch(dirPath) {
1266
1268
 
1267
1269
  if (_jsonMode) process.stdout.write(JSON.stringify(results, null, 2) + '\n');
1268
1270
 
1269
- if (!isProUser() && results.length) showUsageFooter(getUsage().count);
1271
+ if (!isProUser() && results.length) showUsageFooter(getUsage().lifetime);
1270
1272
  if (results.length) showCommunityNudge();
1271
1273
  }
1272
1274
 
@@ -1344,7 +1346,7 @@ function cmdStart() {
1344
1346
  info(` EmailForge — ${url}/email-forge`);
1345
1347
  info(` AudienceDecoder — ${url}/audience`);
1346
1348
  blank();
1347
- info(`Free tier: 5 analyses/day. Unlimited with Pro ($9/mo) → ${UPGRADE_LINKS.free}`);
1349
+ info(`Free tier: 15 analyses total. Unlimited with Pro ($9/mo) → ${UPGRADE_LINKS.free}`);
1348
1350
  info(`Press Ctrl+C to stop`);
1349
1351
  blank();
1350
1352
  openBrowser(url);
@@ -1618,10 +1620,10 @@ function cmdHelp() {
1618
1620
 
1619
1621
  console.log(` ${B}PRICING${R}`);
1620
1622
  blank();
1621
- console.log(` Free 5/day $0`);
1623
+ console.log(` Free 15 total $0`);
1622
1624
  console.log(` Pro Unlimited $9/mo`);
1623
- console.log(` Business 100/day $29/mo`);
1624
- console.log(` Team 500/day $79/mo`);
1625
+ console.log(` Business Unlimited $29/mo`);
1626
+ console.log(` Team Unlimited $79/mo`);
1625
1627
  blank();
1626
1628
  console.log(` Get Pro: ${CY}${UPGRADE_LINKS.free}${R} ${D}# direct checkout${R}`);
1627
1629
  console.log(` Activate: ${CY}content-grade activate <your-license-key>${R}`);
package/bin/telemetry.js CHANGED
@@ -234,44 +234,49 @@ export function telemetryStatus() {
234
234
 
235
235
  // ── Rate limiting ─────────────────────────────────────────────────────────────
236
236
 
237
- const FREE_DAILY_LIMIT = 3;
238
- const PRO_DAILY_LIMIT = 20;
239
- const BUSINESS_DAILY_LIMIT = 100;
240
- const TEAM_DAILY_LIMIT = 500;
237
+ const FREE_LIFETIME_LIMIT = 15;
241
238
 
242
239
  /**
243
- * Check whether the user is within their daily limit for a given command type.
240
+ * Check whether the user is within their lifetime limit for a given command type.
244
241
  * Type: 'analyze' | 'headline'
242
+ * Pro/paid users always pass (unlimited).
243
+ * Free users get 15 lifetime total runs across all command types.
245
244
  * Returns { ok: boolean, used: number, remaining: number, limit: number, isPro: boolean }
246
245
  */
247
- export function checkDailyLimit(type = 'analyze') {
246
+ export function checkUsageLimit(type = 'analyze') {
248
247
  const cfg = loadConfig();
249
- const tier = cfg?.tier || 'free';
250
- const LIMITS = { free: FREE_DAILY_LIMIT, pro: PRO_DAILY_LIMIT, business: BUSINESS_DAILY_LIMIT, team: TEAM_DAILY_LIMIT };
251
- const limit = cfg?.licenseKey ? (LIMITS[tier] || PRO_DAILY_LIMIT) : FREE_DAILY_LIMIT;
252
248
  const isPro = !!cfg?.licenseKey;
249
+ if (isPro) return { ok: true, used: 0, remaining: Infinity, limit: Infinity, isPro: true };
253
250
 
254
- const today = new Date().toISOString().slice(0, 10);
255
- const used = (cfg?.dailyUsage?.date === today ? cfg.dailyUsage[type] ?? 0 : 0);
251
+ const limit = FREE_LIFETIME_LIMIT;
252
+ // Lifetime usage stored in cfg.lifetimeUsage (total across all types)
253
+ const used = cfg?.lifetimeUsage?.total ?? 0;
256
254
  const remaining = limit - used;
257
255
  return { ok: remaining > 0, used, remaining: Math.max(0, remaining), limit, isPro };
258
256
  }
259
257
 
258
+ /** @deprecated Use checkUsageLimit instead */
259
+ export const checkDailyLimit = checkUsageLimit;
260
+
260
261
  /**
261
- * Increment the daily usage counter for a command type after a successful run.
262
- * Returns the new usage count.
262
+ * Increment the lifetime usage counter for a command type after a successful run.
263
+ * Returns the new total usage count.
263
264
  */
264
- export function incrementDailyUsage(type = 'analyze') {
265
+ export function incrementUsage(type = 'analyze') {
265
266
  const cfg = loadConfig() || {};
266
- const today = new Date().toISOString().slice(0, 10);
267
- if (!cfg.dailyUsage || cfg.dailyUsage.date !== today) {
268
- cfg.dailyUsage = { date: today, analyze: 0, headline: 0 };
267
+ if (!cfg.lifetimeUsage) {
268
+ // Migrate from legacy dailyUsage if present
269
+ cfg.lifetimeUsage = { total: 0, analyze: 0, headline: 0 };
269
270
  }
270
- cfg.dailyUsage[type] = (cfg.dailyUsage[type] ?? 0) + 1;
271
+ cfg.lifetimeUsage[type] = (cfg.lifetimeUsage[type] ?? 0) + 1;
272
+ cfg.lifetimeUsage.total = (cfg.lifetimeUsage.total ?? 0) + 1;
271
273
  saveConfig(cfg);
272
- return cfg.dailyUsage[type];
274
+ return cfg.lifetimeUsage.total;
273
275
  }
274
276
 
277
+ /** @deprecated Use incrementUsage instead */
278
+ export const incrementDailyUsage = incrementUsage;
279
+
275
280
  // ── Internal ──────────────────────────────────────────────────────────────────
276
281
 
277
282
  function _write(event) {
@@ -20,10 +20,10 @@ const TIER_LIMITS = {
20
20
  team: TEAM_TIER_LIMIT,
21
21
  };
22
22
  function freeGateMsg(_what) {
23
- return `Free daily limit reached (${FREE_TIER_LIMIT}/day). Upgrade to Pro for unlimited: ${UPGRADE_URL}`;
23
+ return `Free limit reached (${FREE_TIER_LIMIT} per session). Upgrade to Pro for unlimited: ${UPGRADE_URL}`;
24
24
  }
25
25
  function paidGateMsg(limit) {
26
- return `Daily limit reached (${limit}/day). Upgrade for more at content-grade.github.io/Content-Grade/#pricing. Resets at midnight UTC.`;
26
+ return `Rate limit reached (${limit}/day). Upgrade for more at content-grade.github.io/Content-Grade/#pricing. Resets at midnight UTC.`;
27
27
  }
28
28
  function hashIp(ip) {
29
29
  return createHash('sha256').update(ip).digest('hex');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "content-grade",
3
- "version": "1.0.37",
3
+ "version": "1.0.38",
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": {