content-grade 1.0.45 → 1.0.47
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/CONTRIBUTING.md +7 -5
- package/README.md +80 -25
- package/bin/content-grade.js +38 -69
- package/dist/landing.html +24 -15
- package/dist-server/server/db.js +5 -0
- package/dist-server/server/routes/analytics.js +44 -0
- package/package.json +1 -1
package/CONTRIBUTING.md
CHANGED
|
@@ -31,8 +31,8 @@ cd Content-Grade
|
|
|
31
31
|
npm install
|
|
32
32
|
|
|
33
33
|
# Run in development
|
|
34
|
-
node bin/content-grade.js demo # smoke test — no
|
|
35
|
-
node bin/content-grade.js analyze README.md # requires
|
|
34
|
+
node bin/content-grade.js demo # smoke test — runs on built-in sample, no Claude needed
|
|
35
|
+
node bin/content-grade.js analyze README.md # requires Claude CLI logged in
|
|
36
36
|
|
|
37
37
|
# Run all checks (must pass before opening a PR)
|
|
38
38
|
npm test # vitest — all tests green
|
|
@@ -40,16 +40,18 @@ npm run typecheck # zero TypeScript errors
|
|
|
40
40
|
npm run build # vite + tsc — must compile clean
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
**Requirement:** Content-Grade uses [Claude CLI](https://claude.ai/code) for AI analysis — not an API key. Install it with `npm install -g @anthropic-ai/claude-code` and run `claude login`. The `demo` command and all tests run without Claude active.
|
|
44
|
+
|
|
43
45
|
### Environment variables
|
|
44
46
|
|
|
45
47
|
Copy `.env.example` to `.env` if one exists, or set these:
|
|
46
48
|
|
|
47
49
|
| Variable | Required | Description |
|
|
48
50
|
|----------|----------|-------------|
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
51
|
+
| `PORT` | No (default 4000) | Port for the web dashboard server |
|
|
52
|
+
| `STRIPE_SECRET_KEY` | No | Stripe secret key for payment features |
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
No API keys are needed for the analysis tools. All tests and the `demo` command run without any environment variables.
|
|
53
55
|
|
|
54
56
|
## Submitting a pull request
|
|
55
57
|
|
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
[](https://opensource.org/licenses/MIT)
|
|
10
10
|
[](https://nodejs.org)
|
|
11
11
|
[](https://claude.ai/code)
|
|
12
|
+
[](https://github.com/StanislavBG/Content-Grade/issues)
|
|
12
13
|
[](https://github.com/StanislavBG/Content-Grade/discussions)
|
|
13
14
|
[](EARLY_ADOPTERS.md)
|
|
14
15
|
|
|
@@ -47,6 +48,8 @@ content-grade analyze ./post.md --quiet # score number only (for scripts)
|
|
|
47
48
|
content-grade activate # unlock Pro: batch mode, 100/day
|
|
48
49
|
```
|
|
49
50
|
|
|
51
|
+
⭐ **If the quickstart saved you 10 minutes, [star the repo](https://github.com/StanislavBG/Content-Grade) — it helps other developers find it.**
|
|
52
|
+
|
|
50
53
|
---
|
|
51
54
|
|
|
52
55
|
## Upgrade to Pro
|
|
@@ -66,6 +69,8 @@ Most developers hit the limit when they start trusting it enough to use it on ev
|
|
|
66
69
|
|
|
67
70
|
**[Upgrade to Pro → $9/mo](https://buy.stripe.com/4gM14p87GeCh9vn9ks8k80a)**
|
|
68
71
|
|
|
72
|
+
> **Early Adopter?** The first 50 users who share their results in [Show & Tell](https://github.com/StanislavBG/Content-Grade/discussions/new?category=show-and-tell) get **12 months Pro free**. [Details →](#early-adopter-program--12-months-pro-free)
|
|
73
|
+
|
|
69
74
|
**What Pro unlocks in practice:**
|
|
70
75
|
|
|
71
76
|
```bash
|
|
@@ -233,19 +238,62 @@ Free tier: **5 analyses/day**. [Pro ($9/mo)](https://buy.stripe.com/4gM14p87GeCh
|
|
|
233
238
|
|
|
234
239
|
**[All discussions →](https://github.com/StanislavBG/Content-Grade/discussions)**
|
|
235
240
|
|
|
236
|
-
|
|
241
|
+
### Early Adopter Program — 12 months Pro free
|
|
242
|
+
|
|
243
|
+
Over 1,000 developers have installed ContentGrade. **Zero have claimed an Early Adopter seat yet.** That means you're actually first — and there are only 50.
|
|
244
|
+
|
|
245
|
+
The first 50 users who run ContentGrade on real content and share it get **12 months Pro free** ($108 value) plus direct input on what gets built next.
|
|
246
|
+
|
|
247
|
+
**How to claim your seat:**
|
|
248
|
+
1. Run ContentGrade on a real file: `npx content-grade analyze ./your-post.md`
|
|
249
|
+
2. Post in [Show & Tell](https://github.com/StanislavBG/Content-Grade/discussions/new?category=show-and-tell) with `[Early Adopter]` in the title — include your use case, one thing you'd change, and your score
|
|
250
|
+
3. Maintainer responds within 48 hours with Pro access
|
|
251
|
+
|
|
252
|
+
**Short on time?** Just [open an issue](https://github.com/StanislavBG/Content-Grade/issues/new?title=%5BEarly+Adopter%5D+Quick+claim&labels=early-adopter&body=Use+case%3A+%0AScore+I+got%3A+%0AOne+thing+I%27d+change%3A+) with `[Early Adopter]` in the title — same deal, lower friction.
|
|
253
|
+
|
|
254
|
+
Full benefits: [EARLY_ADOPTERS.md](EARLY_ADOPTERS.md)
|
|
237
255
|
|
|
238
256
|
---
|
|
239
257
|
|
|
240
|
-
##
|
|
258
|
+
## Contributing
|
|
259
|
+
|
|
260
|
+
Already using ContentGrade? That makes you the most valuable contributor. You know what's missing.
|
|
261
|
+
|
|
262
|
+
**Three ways to help (pick what fits your time):**
|
|
263
|
+
|
|
264
|
+
| Effort | Action |
|
|
265
|
+
|--------|--------|
|
|
266
|
+
| 2 min | [Open a discussion](https://github.com/StanislavBG/Content-Grade/discussions/new?category=ideas) — describe a workflow gap or missing feature |
|
|
267
|
+
| 20 min | [File a bug report](https://github.com/StanislavBG/Content-Grade/issues/new?template=bug_report.yml) with `--verbose` output |
|
|
268
|
+
| 2 hours | Pick a [`good first issue`](https://github.com/StanislavBG/Content-Grade/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) — scoped tasks with acceptance criteria and a pointer to the right file |
|
|
241
269
|
|
|
242
|
-
|
|
270
|
+
**Quick dev setup:**
|
|
271
|
+
```bash
|
|
272
|
+
git clone https://github.com/StanislavBG/Content-Grade.git
|
|
273
|
+
cd Content-Grade
|
|
274
|
+
npm install
|
|
275
|
+
node bin/content-grade.js demo # smoke test — no Claude needed
|
|
276
|
+
npm test # all tests green
|
|
277
|
+
```
|
|
243
278
|
|
|
244
|
-
|
|
245
|
-
- Are you running it in CI? What score threshold do you use?
|
|
246
|
-
- Any workflow, config, or integration worth sharing?
|
|
279
|
+
Requires [Claude CLI](https://claude.ai/code) for the analysis commands (`claude login` once). No API keys. See [CONTRIBUTING.md](CONTRIBUTING.md) for PR guidelines, code style, and what gets merged.
|
|
247
280
|
|
|
248
|
-
|
|
281
|
+
Contributors who file a useful bug report or land a PR automatically qualify for the [Early Adopter program](#early-adopter-program--12-months-pro-free).
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Show Us Your Results
|
|
286
|
+
|
|
287
|
+
Over 1,000 developers have run ContentGrade. We'd love to hear what you're scoring — blog posts, landing pages, ad copy, emails, documentation.
|
|
288
|
+
|
|
289
|
+
**[→ Post in Show & Tell](https://github.com/StanislavBG/Content-Grade/discussions/new?category=show-and-tell)** (also counts as an Early Adopter claim — 12 months Pro free)
|
|
290
|
+
|
|
291
|
+
Tell us:
|
|
292
|
+
- What content type? What score did you get?
|
|
293
|
+
- Running it in CI? What threshold are you using?
|
|
294
|
+
- Something that surprised you — good or bad?
|
|
295
|
+
|
|
296
|
+
Use cases shared here get featured in this README. We'll credit you by username. And yes — every Show & Tell post qualifies for the [Early Adopter program](EARLY_ADOPTERS.md).
|
|
249
297
|
|
|
250
298
|
---
|
|
251
299
|
|
|
@@ -1036,9 +1084,32 @@ Power users who go beyond early adoption get early access to `@beta` releases, a
|
|
|
1036
1084
|
|
|
1037
1085
|
### Contributing
|
|
1038
1086
|
|
|
1039
|
-
|
|
1087
|
+
All contributions get a review within 48 hours.
|
|
1088
|
+
|
|
1089
|
+
**First contribution in ~15 minutes:**
|
|
1090
|
+
|
|
1091
|
+
```bash
|
|
1092
|
+
git clone https://github.com/StanislavBG/Content-Grade.git
|
|
1093
|
+
cd Content-Grade
|
|
1094
|
+
pnpm install
|
|
1095
|
+
pnpm dev # type-check + test suite
|
|
1096
|
+
```
|
|
1097
|
+
|
|
1098
|
+
Open a PR against `main`. Branch name format: `fix/short-description` or `feat/short-description`.
|
|
1099
|
+
|
|
1100
|
+
**What's most needed right now:**
|
|
1101
|
+
|
|
1102
|
+
| Area | Examples |
|
|
1103
|
+
|------|---------|
|
|
1104
|
+
| Scoring rules | Catch passive voice, jargon density, weak CTAs — add a new rule to the prompt |
|
|
1105
|
+
| CLI output | Better rendering in narrow terminals, CI-friendly formatting |
|
|
1106
|
+
| Bug fixes | Start with a [`good first issue`](https://github.com/StanislavBG/Content-Grade/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) — each has acceptance criteria and the exact file to touch |
|
|
1107
|
+
| Documentation | Fix unclear copy, add real-world examples, document edge cases |
|
|
1108
|
+
| Integrations | GitHub Actions workflows, pre-commit hook improvements, VS Code tasks |
|
|
1109
|
+
|
|
1110
|
+
**Not sure where to start?** Browse **[`good first issue` →](https://github.com/StanislavBG/Content-Grade/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)** — scoped, completable in a few hours, no prior codebase knowledge required.
|
|
1040
1111
|
|
|
1041
|
-
|
|
1112
|
+
See **[CONTRIBUTING.md](CONTRIBUTING.md)** for full code style, test guidelines, and PR checklist.
|
|
1042
1113
|
|
|
1043
1114
|
### Roadmap
|
|
1044
1115
|
|
|
@@ -1070,22 +1141,6 @@ See [docs/social-proof/built-with-badge.md](docs/social-proof/built-with-badge.m
|
|
|
1070
1141
|
|
|
1071
1142
|
---
|
|
1072
1143
|
|
|
1073
|
-
## Community & Feedback
|
|
1074
|
-
|
|
1075
|
-
[](https://github.com/StanislavBG/Content-Grade)
|
|
1076
|
-
|
|
1077
|
-
**1,159 developers have installed Content-Grade — join the community.**
|
|
1078
|
-
|
|
1079
|
-
If Content-Grade saves you time, a ⭐ goes a long way. It helps more developers find the tool and keeps the project active.
|
|
1080
|
-
|
|
1081
|
-
**Early adopter program:** The first 50 seats are still open — early adopters get permanent free Pro tier. [Claim your seat →](https://content-grade.github.io/Content-Grade/#early-adopter)
|
|
1082
|
-
|
|
1083
|
-
**Found a bug?** [Open an issue →](https://github.com/StanislavBG/Content-Grade/issues/new/choose) — every report shapes what gets built next.
|
|
1084
|
-
|
|
1085
|
-
**[GitHub Discussions →](https://github.com/StanislavBG/Content-Grade/discussions)** — questions, ideas, and show & tell. Built something with content-grade? Open a Discussion — we want to see it.
|
|
1086
|
-
|
|
1087
|
-
---
|
|
1088
|
-
|
|
1089
1144
|
## Legal
|
|
1090
1145
|
|
|
1091
1146
|
- [Privacy Policy](https://content-grade.github.io/Content-Grade/privacy.html)
|
package/bin/content-grade.js
CHANGED
|
@@ -79,20 +79,21 @@ function getLicenseTier() {
|
|
|
79
79
|
function isProUser() { return Boolean(getLicenseKey()); }
|
|
80
80
|
|
|
81
81
|
function getUsage() {
|
|
82
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
82
83
|
try {
|
|
83
84
|
const d = JSON.parse(readFileSync(USAGE_FILE, 'utf8'));
|
|
84
|
-
//
|
|
85
|
-
if (d.date) return {
|
|
86
|
-
return {
|
|
87
|
-
} catch { return {
|
|
85
|
+
// Daily reset: if stored date differs from today, start fresh
|
|
86
|
+
if (d.date !== today) return { daily: 0, date: today };
|
|
87
|
+
return { daily: d.daily || 0, date: today };
|
|
88
|
+
} catch { return { daily: 0, date: today }; }
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
function incrementUsage() {
|
|
91
92
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
92
93
|
const u = getUsage();
|
|
93
|
-
u.
|
|
94
|
+
u.daily = (u.daily || 0) + 1;
|
|
94
95
|
writeFileSync(USAGE_FILE, JSON.stringify(u, null, 2), 'utf8');
|
|
95
|
-
return u.
|
|
96
|
+
return u.daily;
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
// Upgrade links — Free → Pro → Business → Team
|
|
@@ -110,7 +111,7 @@ const TIER_NAMES = {
|
|
|
110
111
|
};
|
|
111
112
|
|
|
112
113
|
const TIER_LIMITS = {
|
|
113
|
-
free:
|
|
114
|
+
free: 3,
|
|
114
115
|
pro: Infinity,
|
|
115
116
|
business: Infinity,
|
|
116
117
|
team: Infinity,
|
|
@@ -125,50 +126,29 @@ function getUpgradeMessage() {
|
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
// Show usage-aware upgrade CTA after each free run.
|
|
128
|
-
// count =
|
|
129
|
+
// count = daily usage AFTER this run.
|
|
129
130
|
function showFreeTierCTA(count) {
|
|
130
|
-
const limit = TIER_LIMITS.free; //
|
|
131
|
+
const limit = TIER_LIMITS.free; // 3
|
|
131
132
|
const remaining = Math.max(0, limit - count);
|
|
132
133
|
|
|
133
134
|
// Track that an upgrade prompt was shown after a run (post-run CTA, not a hard block)
|
|
134
|
-
const ctaStrength = remaining === 0 ? 'strong' : remaining <=
|
|
135
|
+
const ctaStrength = remaining === 0 ? 'strong' : remaining <= 1 ? 'warning' : 'nudge';
|
|
135
136
|
recordEvent({ event: 'upgrade_prompt_shown', run_count: count, cta_strength: ctaStrength, cta_context: 'post_run' });
|
|
136
137
|
|
|
137
138
|
blank();
|
|
138
139
|
hr();
|
|
139
140
|
|
|
140
141
|
if (remaining === 0) {
|
|
141
|
-
//
|
|
142
|
-
console.log(` ${
|
|
142
|
+
// Last run of the day — power user nudge
|
|
143
|
+
console.log(` ${YL}${B}${count}/${limit} — that's all your free runs for today.${R}`);
|
|
143
144
|
blank();
|
|
144
|
-
console.log(` ${WH}
|
|
145
|
-
console.log(` ${
|
|
146
|
-
console.log(` ${WH} · Batch mode: grade your entire /posts/ directory in one shot${R}`);
|
|
147
|
-
console.log(` ${WH} · CI integration: exit codes + JSON/HTML output${R}`);
|
|
145
|
+
console.log(` ${WH} Pro: 50 analyses/day · Business: unlimited · $9/mo, cancel anytime${R}`);
|
|
146
|
+
console.log(` ${MG}${B}→ ${UPGRADE_LINKS.free}${R}`);
|
|
148
147
|
blank();
|
|
149
|
-
console.log(` ${
|
|
150
|
-
blank();
|
|
151
|
-
console.log(` ${D}After checkout — 2 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. Run: ${CY}content-grade activate <your-key>${R}`);
|
|
154
|
-
console.log(` ${D} Already have a key? Run step 2 now.${R}`);
|
|
155
|
-
} else if (remaining <= 2) {
|
|
156
|
-
// 1-2 runs left — loss aversion + specific Pro value
|
|
157
|
-
console.log(` ${YL}${B}${count}/${limit} runs used · ${remaining} free analysis${remaining === 1 ? '' : 'es'} left${R}`);
|
|
158
|
-
blank();
|
|
159
|
-
console.log(` ${WH}${B}Pro: no cap, no resets — grade every piece you publish${R}`);
|
|
160
|
-
console.log(` ${WH} · Batch /posts/ directories · CI exit codes · JSON output${R}`);
|
|
161
|
-
console.log(` ${MG}${B}→ Get Pro before you run out ($9/mo): ${CY}${UPGRADE_LINKS.free}${R}`);
|
|
148
|
+
console.log(` ${D}Or come back tomorrow for ${limit} more free runs.${R}`);
|
|
162
149
|
} else {
|
|
163
|
-
// Runs
|
|
164
|
-
|
|
165
|
-
`Pro: no cap — grade every piece you publish, no resets`,
|
|
166
|
-
`Pro: batch entire directories at once · no limits, ever`,
|
|
167
|
-
`Pro: CI integration · exit codes + JSON output · unlimited runs`,
|
|
168
|
-
];
|
|
169
|
-
console.log(` ${WH}${count}/${limit} runs used · ${remaining} remaining${R}`);
|
|
170
|
-
console.log(` ${WH} ${nudgeBenefits[count % 3]}${R}`);
|
|
171
|
-
console.log(` ${MG}→ Get Pro ($9/mo): ${CY}${UPGRADE_LINKS.free}${R}`);
|
|
150
|
+
// Runs remaining — compact single-line footer
|
|
151
|
+
console.log(` ${D}[ ${count}/${limit} free runs today · ${remaining} remaining · Pro: 50/day ($9/mo) → ${UPGRADE_LINKS.free} ]${R}`);
|
|
172
152
|
}
|
|
173
153
|
hr();
|
|
174
154
|
maybeShowFeedbackCTA(count);
|
|
@@ -239,64 +219,53 @@ function showCommunityNudge() {
|
|
|
239
219
|
}
|
|
240
220
|
|
|
241
221
|
// Single-line usage counter appended after every command run.
|
|
242
|
-
// count =
|
|
222
|
+
// count = daily runs used (after this run).
|
|
243
223
|
function showUsageFooter(count) {
|
|
244
224
|
if (isProUser()) return;
|
|
245
225
|
const limit = TIER_LIMITS.free;
|
|
246
226
|
const remaining = Math.max(0, limit - count);
|
|
247
227
|
blank();
|
|
248
228
|
if (remaining === 0) {
|
|
249
|
-
console.log(` ${RD}[
|
|
250
|
-
} else if (remaining <=
|
|
251
|
-
console.log(` ${YL}[ ${remaining} free analysis
|
|
229
|
+
console.log(` ${RD}[ ${limit}/${limit} free analyses used today · Pro: 50/day, Business: unlimited — $9/mo → ${UPGRADE_LINKS.free} ]${R}`);
|
|
230
|
+
} else if (remaining <= 1) {
|
|
231
|
+
console.log(` ${YL}[ ${remaining} free analysis remaining today · Pro: 50/day, Business: unlimited — $9/mo → ${UPGRADE_LINKS.free} ]${R}`);
|
|
252
232
|
} else {
|
|
253
|
-
console.log(` ${D}[ ${count}/${limit} free analyses
|
|
233
|
+
console.log(` ${D}[ ${count}/${limit} free analyses today · ${remaining} remaining · Pro: 50/day ($9/mo) → ${UPGRADE_LINKS.free} ]${R}`);
|
|
254
234
|
}
|
|
255
235
|
maybeShowEarlyAdopterCTA(count);
|
|
256
236
|
maybeShowFeedbackCTA(count);
|
|
257
237
|
}
|
|
258
238
|
|
|
259
|
-
// Returns { ok: boolean, count: number, limit: number } for tier-aware
|
|
260
|
-
// Used by batch mode to stop processing when the
|
|
239
|
+
// Returns { ok: boolean, count: number, limit: number } for tier-aware daily limit checks.
|
|
240
|
+
// Used by batch mode to stop processing when the daily limit is reached.
|
|
261
241
|
// Pro tier has Infinity limit and always returns ok: true.
|
|
262
|
-
function
|
|
242
|
+
function checkDailyLimit() {
|
|
263
243
|
const tier = getLicenseTier();
|
|
264
244
|
const limit = TIER_LIMITS[tier] ?? TIER_LIMITS.free;
|
|
265
245
|
if (limit === Infinity) return { ok: true, count: 0, limit: Infinity };
|
|
266
246
|
const usage = getUsage();
|
|
267
|
-
const count = usage.
|
|
247
|
+
const count = usage.daily || 0;
|
|
268
248
|
return { ok: count < limit, count, limit };
|
|
269
249
|
}
|
|
270
250
|
|
|
271
|
-
// Block a run before it starts if the free
|
|
251
|
+
// Block a run before it starts if the free daily limit is exhausted.
|
|
272
252
|
// Returns true (blocked) and prints a visually distinct upgrade prompt.
|
|
273
253
|
// Returns false if the user may proceed.
|
|
274
254
|
function checkFreeTierLimit() {
|
|
275
255
|
if (isProUser()) return false;
|
|
276
256
|
const usage = getUsage();
|
|
277
257
|
const limit = TIER_LIMITS.free;
|
|
278
|
-
if (usage.
|
|
258
|
+
if (usage.daily < limit) return false;
|
|
279
259
|
|
|
280
260
|
// Track funnel event — user hit the limit and saw the upgrade prompt
|
|
281
261
|
recordEvent({ event: 'free_limit_hit', version: _version });
|
|
282
|
-
recordEvent({ event: 'upgrade_prompt_shown', run_count: usage.
|
|
262
|
+
recordEvent({ event: 'upgrade_prompt_shown', run_count: usage.daily });
|
|
283
263
|
|
|
284
264
|
blank();
|
|
285
|
-
|
|
286
|
-
console.log(` ${
|
|
287
|
-
|
|
288
|
-
console.log(` ${
|
|
289
|
-
console.log(` ${WH} · No limit — run as many analyses as you need${R}`);
|
|
290
|
-
console.log(` ${WH} · Batch mode: grade your entire /posts/ directory in one shot${R}`);
|
|
291
|
-
console.log(` ${WH} · CI integration: exit codes + JSON/HTML output${R}`);
|
|
292
|
-
blank();
|
|
293
|
-
console.log(` ${MG}${B}→ Get Pro ($9/mo, cancel anytime): ${CY}${UPGRADE_LINKS.free}${R}`);
|
|
294
|
-
blank();
|
|
295
|
-
console.log(` ${D}After checkout — 2 steps to unlock:${R}`);
|
|
296
|
-
console.log(` ${D} 1. Get your key: ${CY}https://content-grade.onrender.com/my-license${R}`);
|
|
297
|
-
console.log(` ${D} 2. Run: ${CY}content-grade activate <your-key>${R}`);
|
|
298
|
-
console.log(` ${D} Already have a key? Run step 2 now.${R}`);
|
|
299
|
-
hr();
|
|
265
|
+
console.log(` ${RD}${B}${limit}/${limit} free analyses used today.${R}`);
|
|
266
|
+
console.log(` ${WH}Pro: 50/day · Business: unlimited — $9/mo, cancel anytime${R}`);
|
|
267
|
+
console.log(` ${MG}${B}→ ${UPGRADE_LINKS.free}${R}`);
|
|
268
|
+
console.log(` ${D}After purchase: content-grade activate <key>${R}`);
|
|
300
269
|
blank();
|
|
301
270
|
return true;
|
|
302
271
|
}
|
|
@@ -1229,12 +1198,12 @@ async function cmdBatch(dirPath) {
|
|
|
1229
1198
|
const rel = f.startsWith(process.cwd()) ? f.slice(process.cwd().length + 1) : f;
|
|
1230
1199
|
process.stdout.write(` ${D}[${i + 1}/${files.length}]${R} ${rel}...`);
|
|
1231
1200
|
|
|
1232
|
-
// Guard: check
|
|
1233
|
-
const batchLimitCheck =
|
|
1201
|
+
// Guard: check daily limit before each analysis
|
|
1202
|
+
const batchLimitCheck = checkDailyLimit();
|
|
1234
1203
|
if (!batchLimitCheck.ok) {
|
|
1235
1204
|
process.stdout.write(` ${YL}limit reached${R}\n`);
|
|
1236
1205
|
blank();
|
|
1237
|
-
warn(`Free limit reached (${batchLimitCheck.count}/${batchLimitCheck.limit}
|
|
1206
|
+
warn(`Free limit reached (${batchLimitCheck.count}/${batchLimitCheck.limit} analyses used today). Remaining files skipped.`);
|
|
1238
1207
|
break;
|
|
1239
1208
|
}
|
|
1240
1209
|
|
|
@@ -1283,7 +1252,7 @@ async function cmdBatch(dirPath) {
|
|
|
1283
1252
|
|
|
1284
1253
|
if (_jsonMode) process.stdout.write(JSON.stringify(results, null, 2) + '\n');
|
|
1285
1254
|
|
|
1286
|
-
if (!isProUser() && results.length) showUsageFooter(getUsage().
|
|
1255
|
+
if (!isProUser() && results.length) showUsageFooter(getUsage().daily);
|
|
1287
1256
|
if (results.length) showCommunityNudge();
|
|
1288
1257
|
}
|
|
1289
1258
|
|
package/dist/landing.html
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@type": "Offer",
|
|
37
37
|
"price": "0",
|
|
38
38
|
"priceCurrency": "USD",
|
|
39
|
-
"description": "Free:
|
|
39
|
+
"description": "Free: 15 analyses total. Pro: $9/mo, unlimited. Business: $29/mo, unlimited. Team: $79/mo, unlimited + shared workspace."
|
|
40
40
|
},
|
|
41
41
|
"featureList": [
|
|
42
42
|
"Headline analyzer with framework-based scoring",
|
|
@@ -631,13 +631,14 @@
|
|
|
631
631
|
|
|
632
632
|
.pricing-grid {
|
|
633
633
|
display: grid;
|
|
634
|
-
grid-template-columns: repeat(
|
|
635
|
-
gap:
|
|
636
|
-
max-width:
|
|
634
|
+
grid-template-columns: repeat(4, 1fr);
|
|
635
|
+
gap: 20px;
|
|
636
|
+
max-width: 1160px;
|
|
637
637
|
margin: 0 auto;
|
|
638
638
|
}
|
|
639
639
|
|
|
640
|
-
@media (max-width:
|
|
640
|
+
@media (max-width: 1100px) { .pricing-grid { grid-template-columns: repeat(2, 1fr); max-width: 720px; } }
|
|
641
|
+
@media (max-width: 600px) { .pricing-grid { grid-template-columns: 1fr; max-width: 480px; } }
|
|
641
642
|
|
|
642
643
|
/* === SOCIAL PROOF === */
|
|
643
644
|
.social-proof {
|
|
@@ -1016,7 +1017,7 @@
|
|
|
1016
1017
|
</div>
|
|
1017
1018
|
|
|
1018
1019
|
<div class="hero-meta">
|
|
1019
|
-
<span>Free ·
|
|
1020
|
+
<span>Free · 15 analyses to start</span>
|
|
1020
1021
|
<span class="hero-meta-sep">·</span>
|
|
1021
1022
|
<span>Claude CLI required · Zero data leaves your machine</span>
|
|
1022
1023
|
<span class="hero-meta-sep">·</span>
|
|
@@ -1195,7 +1196,7 @@
|
|
|
1195
1196
|
<div class="step">
|
|
1196
1197
|
<div class="step-num">3</div>
|
|
1197
1198
|
<h3>Launch the full dashboard</h3>
|
|
1198
|
-
<p>Start the web interface to access all 6 tools — HeadlineGrader, PageRoast, AdScorer, ThreadGrader, EmailForge, and AudienceDecoder. Free tier:
|
|
1199
|
+
<p>Start the web interface to access all 6 tools — HeadlineGrader, PageRoast, AdScorer, ThreadGrader, EmailForge, and AudienceDecoder. Free tier: 15 analyses total across all tools.</p>
|
|
1199
1200
|
<div class="step-code">npx content-grade start</div>
|
|
1200
1201
|
</div>
|
|
1201
1202
|
</div>
|
|
@@ -1208,7 +1209,7 @@
|
|
|
1208
1209
|
<div class="pricing-header">
|
|
1209
1210
|
<div class="section-eyebrow">Pricing</div>
|
|
1210
1211
|
<h2>Start free. Upgrade when it pays for itself.</h2>
|
|
1211
|
-
<p class="pricing-lead">
|
|
1212
|
+
<p class="pricing-lead">15 free analyses to start. No credit card, no signup, no API key required.</p>
|
|
1212
1213
|
</div>
|
|
1213
1214
|
|
|
1214
1215
|
<div class="pricing-grid">
|
|
@@ -1218,7 +1219,7 @@
|
|
|
1218
1219
|
<div class="plan-period">forever · no signup required</div>
|
|
1219
1220
|
<a href="https://www.npmjs.com/package/content-grade" class="plan-cta">Install free →</a>
|
|
1220
1221
|
<ul class="plan-features">
|
|
1221
|
-
<li><span class="check">✓</span> <strong>
|
|
1222
|
+
<li><span class="check">✓</span> <strong>15 analyses total</strong></li>
|
|
1222
1223
|
<li><span class="check">✓</span> All 6 tools (CLI + dashboard)</li>
|
|
1223
1224
|
<li><span class="check">✓</span> Full framework-based scoring</li>
|
|
1224
1225
|
<li><span class="check">✓</span> AI rewrites with technique labels</li>
|
|
@@ -1246,10 +1247,10 @@
|
|
|
1246
1247
|
<div class="plan">
|
|
1247
1248
|
<div class="plan-name">BUSINESS</div>
|
|
1248
1249
|
<div class="plan-price">$29<span>/mo</span></div>
|
|
1249
|
-
<div class="plan-period">
|
|
1250
|
+
<div class="plan-period">Unlimited analyses · billed monthly</div>
|
|
1250
1251
|
<a href="https://buy.stripe.com/bJefZjafO2Tz36Z2W48k80b" class="plan-cta" target="_blank" rel="noopener">Get Business →</a>
|
|
1251
1252
|
<ul class="plan-features">
|
|
1252
|
-
<li><span class="check">✓</span> <strong>
|
|
1253
|
+
<li><span class="check">✓</span> <strong>Unlimited analyses</strong></li>
|
|
1253
1254
|
<li><span class="check">✓</span> Everything in Pro</li>
|
|
1254
1255
|
<li><span class="check">✓</span> A/B compare with conversion predictions</li>
|
|
1255
1256
|
<li><span class="check">✓</span> Best value per analysis ($0.29/analysis)</li>
|
|
@@ -1259,16 +1260,24 @@
|
|
|
1259
1260
|
<div class="plan">
|
|
1260
1261
|
<div class="plan-name">TEAM</div>
|
|
1261
1262
|
<div class="plan-price">$79<span>/mo</span></div>
|
|
1262
|
-
<div class="plan-period">
|
|
1263
|
+
<div class="plan-period">Unlimited analyses · billed monthly</div>
|
|
1263
1264
|
<a href="https://buy.stripe.com/cNiaEZfA8cu9bDv4088k80c" class="plan-cta" target="_blank" rel="noopener">Get Team →</a>
|
|
1264
1265
|
<ul class="plan-features">
|
|
1265
|
-
<li><span class="check">✓</span> <strong>
|
|
1266
|
+
<li><span class="check">✓</span> <strong>Unlimited analyses</strong></li>
|
|
1266
1267
|
<li><span class="check">✓</span> Everything in Business</li>
|
|
1267
1268
|
<li><span class="check">✓</span> Shared team workspace</li>
|
|
1268
1269
|
<li><span class="check">✓</span> Dedicated support + SLA</li>
|
|
1269
1270
|
</ul>
|
|
1270
1271
|
</div>
|
|
1271
1272
|
</div>
|
|
1273
|
+
|
|
1274
|
+
<div style="margin-top:32px; padding:20px 24px; background:rgba(255,255,255,0.04); border:1px solid rgba(255,255,255,0.1); border-radius:10px; max-width:560px; margin-left:auto; margin-right:auto; text-align:left;">
|
|
1275
|
+
<div style="font-size:13px; color:rgba(255,255,255,0.5); text-transform:uppercase; letter-spacing:1px; margin-bottom:10px;">After checkout — 2 steps to unlock Pro</div>
|
|
1276
|
+
<ol style="padding-left:20px; color:rgba(255,255,255,0.8); font-size:15px; line-height:1.9; margin:0;">
|
|
1277
|
+
<li>Get your license key: <a href="https://content-grade.onrender.com/my-license" target="_blank" rel="noopener" style="color:#a78bfa;">content-grade.onrender.com/my-license</a></li>
|
|
1278
|
+
<li>Run: <code style="background:rgba(255,255,255,0.08); padding:2px 8px; border-radius:4px; font-size:13px;">content-grade activate <your-key></code></li>
|
|
1279
|
+
</ol>
|
|
1280
|
+
</div>
|
|
1272
1281
|
</div>
|
|
1273
1282
|
</section>
|
|
1274
1283
|
|
|
@@ -1293,7 +1302,7 @@
|
|
|
1293
1302
|
<details>
|
|
1294
1303
|
<summary>Do I need an Anthropic API key or a paid Claude account?</summary>
|
|
1295
1304
|
<div class="faq-answer">
|
|
1296
|
-
No API key required. ContentGrade runs on <strong>Claude CLI</strong> — install it for free from claude.ai/code and log in with a Claude account. A free Claude account is sufficient for the free tier (
|
|
1305
|
+
No API key required. ContentGrade runs on <strong>Claude CLI</strong> — install it for free from claude.ai/code and log in with a Claude account. A free Claude account is sufficient for the free tier (15 analyses total across all tools).<br /><br />
|
|
1297
1306
|
There's no per-query Anthropic billing. You're not routing calls through the API console or accumulating usage charges. Claude CLI handles authentication locally, which is also why your content never leaves your machine.
|
|
1298
1307
|
</div>
|
|
1299
1308
|
</details>
|
|
@@ -1399,7 +1408,7 @@
|
|
|
1399
1408
|
<section class="final-cta">
|
|
1400
1409
|
<div class="container">
|
|
1401
1410
|
<h2>Run your best headline.<br />See where it actually stands.</h2>
|
|
1402
|
-
<p>Free ·
|
|
1411
|
+
<p>Free · 15 analyses to start · No API key · Claude CLI only</p>
|
|
1403
1412
|
<div style="display: flex; gap: 16px; justify-content: center; flex-wrap: wrap;">
|
|
1404
1413
|
<a href="https://github.com/Content-Grade/Content-Grade" class="btn-primary">
|
|
1405
1414
|
Install free — npx content-grade
|
package/dist-server/server/db.js
CHANGED
|
@@ -142,4 +142,9 @@ function migrate(db) {
|
|
|
142
142
|
db.exec(`ALTER TABLE cli_telemetry ADD COLUMN run_count INTEGER`);
|
|
143
143
|
}
|
|
144
144
|
catch { }
|
|
145
|
+
// Preflight Suite: identify which tool sent the ping
|
|
146
|
+
try {
|
|
147
|
+
db.exec(`ALTER TABLE cli_telemetry ADD COLUMN package TEXT`);
|
|
148
|
+
}
|
|
149
|
+
catch { }
|
|
145
150
|
}
|
|
@@ -54,6 +54,50 @@ export function registerAnalyticsRoutes(app) {
|
|
|
54
54
|
}
|
|
55
55
|
return { ok: true };
|
|
56
56
|
});
|
|
57
|
+
// ── Preflight Suite telemetry — fire-and-forget ping per CLI run ─────────
|
|
58
|
+
// Accepts: { event, package, command, version, node_version, platform, anonymous_id }
|
|
59
|
+
// No PII. Never stores IP addresses. Always returns 200.
|
|
60
|
+
app.post('/telemetry', async (req) => {
|
|
61
|
+
try {
|
|
62
|
+
const body = req.body;
|
|
63
|
+
const anonymousId = body?.anonymous_id ? String(body.anonymous_id).slice(0, 64) : null;
|
|
64
|
+
if (!anonymousId)
|
|
65
|
+
return { ok: true };
|
|
66
|
+
const db = getDb();
|
|
67
|
+
db.prepare(`
|
|
68
|
+
INSERT INTO cli_telemetry
|
|
69
|
+
(install_id, package, event, command, version, platform, node_version, created_at)
|
|
70
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
|
71
|
+
`).run(anonymousId, body?.package ? String(body.package).slice(0, 64) : null, body?.event ? String(body.event).slice(0, 64) : 'command_run', body?.command ? String(body.command).slice(0, 64) : null, body?.version ? String(body.version).slice(0, 32) : null, body?.platform ? String(body.platform).slice(0, 32) : null, body?.node_version ? String(body.node_version).slice(0, 32) : null);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// never fail — telemetry is non-critical
|
|
75
|
+
}
|
|
76
|
+
return { ok: true };
|
|
77
|
+
});
|
|
78
|
+
// ── Preflight Suite telemetry/events — the endpoint all CLI tools point to ─
|
|
79
|
+
// stepproof, agent-comply, agent-gate, agent-trace all POST here.
|
|
80
|
+
// Accepts camelCase fields (installId, nodeVersion) as sent by the CLIs.
|
|
81
|
+
// Always returns 200 — fire-and-forget compatible.
|
|
82
|
+
app.post('/api/telemetry/events', async (req) => {
|
|
83
|
+
try {
|
|
84
|
+
const body = req.body;
|
|
85
|
+
const installId = body?.installId ?? body?.install_id;
|
|
86
|
+
if (!body || typeof installId !== 'string' || !installId) {
|
|
87
|
+
return { ok: true };
|
|
88
|
+
}
|
|
89
|
+
const db = getDb();
|
|
90
|
+
db.prepare(`
|
|
91
|
+
INSERT INTO cli_telemetry
|
|
92
|
+
(install_id, package, event, command, success, version, platform, node_version, exit_code, duration_ms)
|
|
93
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
94
|
+
`).run(String(installId).slice(0, 64), body.package ? String(body.package).slice(0, 64) : null, body.event ? String(body.event).slice(0, 64) : 'run', body.command ? String(body.command).slice(0, 64) : null, typeof body.success === 'boolean' ? (body.success ? 1 : 0) : null, body.version ? String(body.version).slice(0, 32) : null, body.platform ? String(body.platform).slice(0, 32) : null, (body.nodeVersion ?? body.node_version) ? String(body.nodeVersion ?? body.node_version).slice(0, 32) : null, typeof body.exit_code === 'number' ? body.exit_code : null, typeof body.duration_ms === 'number' ? Math.round(body.duration_ms) : null);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// never fail — telemetry is non-critical
|
|
98
|
+
}
|
|
99
|
+
return { ok: true };
|
|
100
|
+
});
|
|
57
101
|
// ── CLI telemetry receiver ────────────────────────────────────────────────
|
|
58
102
|
// Receives events from CLI users who have opted in to telemetry.
|
|
59
103
|
// Always returns 200 — never interrupt a CLI session for analytics.
|
package/package.json
CHANGED