content-grade 1.0.13 → 1.0.15
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 +2 -1
- package/bin/content-grade.js +91 -32
- package/bin/telemetry.js +6 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ npx content-grade 'blog/**/*.md'
|
|
|
63
63
|
|
|
64
64
|
**JSON output for CI integration:**
|
|
65
65
|
```bash
|
|
66
|
-
npx content-grade README.md --
|
|
66
|
+
npx content-grade README.md --json
|
|
67
67
|
# Returns structured JSON with score, grade, dimensions, and improvements
|
|
68
68
|
# Exit code 1 if score < 50 — useful for blocking low-quality merges
|
|
69
69
|
```
|
|
@@ -549,6 +549,7 @@ tests/
|
|
|
549
549
|
|
|
550
550
|
**Documentation:**
|
|
551
551
|
- [`docs/getting-started.md`](./docs/getting-started.md) — step-by-step first-run guide
|
|
552
|
+
- [`docs/cli-reference.md`](./docs/cli-reference.md) — full CLI reference: all commands, flags, output schema, exit codes
|
|
552
553
|
- [`docs/examples.md`](./docs/examples.md) — real-world examples for all 6 tools, CI/CD workflows, Node.js integration
|
|
553
554
|
- [`docs/api.md`](./docs/api.md) — full REST API reference
|
|
554
555
|
|
package/bin/content-grade.js
CHANGED
|
@@ -46,6 +46,11 @@ function getLicenseKey() {
|
|
|
46
46
|
return process.env.CONTENT_GRADE_KEY || loadConfig().licenseKey || null;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
function getLicenseTier() {
|
|
50
|
+
const cfg = loadConfig();
|
|
51
|
+
return cfg.tier || 'free'; // free | starter | pro | team
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
function isProUser() { return Boolean(getLicenseKey()); }
|
|
50
55
|
|
|
51
56
|
function getTodayKey() { return new Date().toISOString().slice(0, 10); }
|
|
@@ -66,13 +71,45 @@ function incrementUsage() {
|
|
|
66
71
|
return u.count;
|
|
67
72
|
}
|
|
68
73
|
|
|
69
|
-
|
|
74
|
+
// Usage tiers — daily limits per plan
|
|
75
|
+
const TIER_LIMITS = {
|
|
76
|
+
free: 5, // 5/day
|
|
77
|
+
starter: 20, // ~600/mo ≈ 50/mo was too low for daily use
|
|
78
|
+
pro: 50, // ~1500/mo
|
|
79
|
+
team: 100, // hard cap even for top tier — no abuse
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const UPGRADE_LINKS = {
|
|
83
|
+
free: 'https://buy.stripe.com/eVq6oJbjSdyd6jbaow8k805', // Starter $9/mo
|
|
84
|
+
starter: 'https://buy.stripe.com/00w9AV87G65L7nf54c8k806', // Pro $29/mo
|
|
85
|
+
pro: 'https://buy.stripe.com/4gM28t73C79P0YR7ck8k807', // Team $79/mo
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const TIER_NAMES = {
|
|
89
|
+
free: 'Free (5/day)',
|
|
90
|
+
starter: 'Starter — $9/mo (20/day)',
|
|
91
|
+
pro: 'Pro — $29/mo (50/day)',
|
|
92
|
+
team: 'Team — $79/mo (100/day)',
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
function getDailyLimit() {
|
|
96
|
+
const tier = getLicenseTier();
|
|
97
|
+
return TIER_LIMITS[tier] || TIER_LIMITS.free;
|
|
98
|
+
}
|
|
70
99
|
|
|
71
100
|
function checkDailyLimit() {
|
|
72
|
-
|
|
101
|
+
const limit = getDailyLimit();
|
|
73
102
|
const u = getUsage();
|
|
74
|
-
if (u.count >=
|
|
75
|
-
return { ok: true, count: u.count, limit
|
|
103
|
+
if (u.count >= limit) return { ok: false, count: u.count, limit };
|
|
104
|
+
return { ok: true, count: u.count, limit };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function getUpgradeMessage() {
|
|
108
|
+
const tier = getLicenseTier();
|
|
109
|
+
const link = UPGRADE_LINKS[tier];
|
|
110
|
+
if (!link) return ''; // team tier — no upgrade available
|
|
111
|
+
const nextTier = tier === 'free' ? 'Starter ($9/mo, 20/day)' : tier === 'starter' ? 'Pro ($29/mo, 50/day)' : 'Team ($79/mo, 100/day)';
|
|
112
|
+
return `Upgrade to ${nextTier} → ${link}`;
|
|
76
113
|
}
|
|
77
114
|
|
|
78
115
|
let _version = '1.0.0';
|
|
@@ -252,18 +289,23 @@ SCORING CALIBRATION:
|
|
|
252
289
|
|
|
253
290
|
async function cmdAnalyze(filePath) {
|
|
254
291
|
if (!filePath) {
|
|
292
|
+
if (_demoMode) {
|
|
293
|
+
return cmdDemo();
|
|
294
|
+
}
|
|
255
295
|
blank();
|
|
256
296
|
fail(`No file specified.`);
|
|
257
297
|
blank();
|
|
258
|
-
console.log(` Usage
|
|
298
|
+
console.log(` ${B}Usage:${R}`);
|
|
259
299
|
console.log(` ${CY}content-grade analyze <file>${R} ${D}(or: check <file>)${R}`);
|
|
260
300
|
blank();
|
|
261
|
-
console.log(` Examples
|
|
262
|
-
console.log(` ${
|
|
263
|
-
console.log(` ${
|
|
264
|
-
console.log(` ${
|
|
301
|
+
console.log(` ${B}Examples:${R}`);
|
|
302
|
+
console.log(` ${CY}content-grade analyze ./blog-post.md${R}`);
|
|
303
|
+
console.log(` ${CY}content-grade check ./email-draft.txt${R}`);
|
|
304
|
+
console.log(` ${CY}content-grade analyze ~/landing-page-copy.md${R}`);
|
|
265
305
|
blank();
|
|
266
|
-
console.log(` ${
|
|
306
|
+
console.log(` ${GN}Tip:${R} See it in action first — no file needed:`);
|
|
307
|
+
console.log(` ${CY}content-grade --demo${R} ${D}# live AI analysis on built-in sample${R}`);
|
|
308
|
+
console.log(` ${CY}content-grade demo${R} ${D}# same thing${R}`);
|
|
267
309
|
blank();
|
|
268
310
|
process.exit(1);
|
|
269
311
|
}
|
|
@@ -339,13 +381,13 @@ async function cmdAnalyze(filePath) {
|
|
|
339
381
|
const limitCheck = checkDailyLimit();
|
|
340
382
|
if (!limitCheck.ok) {
|
|
341
383
|
blank();
|
|
342
|
-
fail(`Daily limit reached (${limitCheck.count}/${limitCheck.limit}
|
|
384
|
+
fail(`Daily limit reached (${limitCheck.count}/${limitCheck.limit} runs used today on ${TIER_NAMES[getLicenseTier()]} plan).`);
|
|
343
385
|
blank();
|
|
344
386
|
console.log(` ${B}Options:${R}`);
|
|
345
387
|
console.log(` ${D}· Wait until tomorrow (limit resets at midnight)${R}`);
|
|
346
|
-
console.log(` ${D}·
|
|
388
|
+
console.log(` ${D}· Upgrade your plan: ${CY}content-grade activate${R}`);
|
|
347
389
|
blank();
|
|
348
|
-
console.log(` ${MG}
|
|
390
|
+
console.log(` ${MG}${getUpgradeMessage()}${R}`);
|
|
349
391
|
blank();
|
|
350
392
|
process.exit(1);
|
|
351
393
|
}
|
|
@@ -524,9 +566,9 @@ async function cmdAnalyze(filePath) {
|
|
|
524
566
|
console.log(` ${CY}content-grade analyze https://yoursite.com/post${R} ${D}# audit any live URL${R}`);
|
|
525
567
|
blank();
|
|
526
568
|
if (remaining <= 10) {
|
|
527
|
-
console.log(` ${YL}${remaining}
|
|
569
|
+
console.log(` ${YL}${remaining} runs left today (${TIER_NAMES[getLicenseTier()]}).${R} ${MG}${getUpgradeMessage()}${R}`);
|
|
528
570
|
} else {
|
|
529
|
-
console.log(` ${D}${remaining}
|
|
571
|
+
console.log(` ${D}${remaining} runs left today. ${getUpgradeMessage()}${R}`);
|
|
530
572
|
}
|
|
531
573
|
}
|
|
532
574
|
|
|
@@ -604,13 +646,13 @@ async function cmdHeadline(text) {
|
|
|
604
646
|
const limitCheck = checkDailyLimit();
|
|
605
647
|
if (!limitCheck.ok) {
|
|
606
648
|
blank();
|
|
607
|
-
fail(`Daily limit reached (${limitCheck.count}/${limitCheck.limit}
|
|
649
|
+
fail(`Daily limit reached (${limitCheck.count}/${limitCheck.limit} runs used today on ${TIER_NAMES[getLicenseTier()]} plan).`);
|
|
608
650
|
blank();
|
|
609
651
|
console.log(` ${B}Options:${R}`);
|
|
610
652
|
console.log(` ${D}· Wait until tomorrow (limit resets at midnight)${R}`);
|
|
611
|
-
console.log(` ${D}·
|
|
653
|
+
console.log(` ${D}· Upgrade your plan: ${CY}content-grade activate${R}`);
|
|
612
654
|
blank();
|
|
613
|
-
console.log(` ${MG}
|
|
655
|
+
console.log(` ${MG}${getUpgradeMessage()}${R}`);
|
|
614
656
|
blank();
|
|
615
657
|
process.exit(1);
|
|
616
658
|
}
|
|
@@ -700,7 +742,7 @@ async function cmdHeadline(text) {
|
|
|
700
742
|
console.log(` ${D}Compare two headlines: ${CY}content-grade start${R} → HeadlineGrader compare${R}`);
|
|
701
743
|
blank();
|
|
702
744
|
if (!isProUser()) {
|
|
703
|
-
console.log(` ${MG}
|
|
745
|
+
console.log(` ${MG}${getUpgradeMessage()}${R}`);
|
|
704
746
|
blank();
|
|
705
747
|
}
|
|
706
748
|
}
|
|
@@ -1123,7 +1165,7 @@ function cmdStart() {
|
|
|
1123
1165
|
info(` EmailForge — ${url}/email-forge`);
|
|
1124
1166
|
info(` AudienceDecoder — ${url}/audience`);
|
|
1125
1167
|
blank();
|
|
1126
|
-
info(`Free tier:
|
|
1168
|
+
info(`Free tier: 5 analyses/day. Upgrade at ${url}`);
|
|
1127
1169
|
info(`Press Ctrl+C to stop`);
|
|
1128
1170
|
blank();
|
|
1129
1171
|
openBrowser(url);
|
|
@@ -1302,10 +1344,10 @@ function cmdHelp() {
|
|
|
1302
1344
|
blank();
|
|
1303
1345
|
console.log(` ${B}QUICK START${R}`);
|
|
1304
1346
|
blank();
|
|
1347
|
+
console.log(` ${CY}npx content-grade --demo${R} ${D}# live AI demo — no file needed, see it first${R}`);
|
|
1305
1348
|
console.log(` ${CY}npx content-grade headline "Your Headline Here"${R} ${D}# grade a headline (fastest, ~5s)${R}`);
|
|
1306
1349
|
console.log(` ${CY}npx content-grade analyze ./my-post.md${R} ${D}# full AI analysis of a file (~20s)${R}`);
|
|
1307
1350
|
console.log(` ${CY}npx content-grade analyze https://example.com${R} ${D}# audit any live URL (~20s)${R}`);
|
|
1308
|
-
console.log(` ${CY}npx content-grade demo${R} ${D}# live demo on sample content${R}`);
|
|
1309
1351
|
console.log(` ${CY}npx content-grade start${R} ${D}# launch web dashboard (6 tools)${R}`);
|
|
1310
1352
|
blank();
|
|
1311
1353
|
|
|
@@ -1348,6 +1390,7 @@ function cmdHelp() {
|
|
|
1348
1390
|
blank();
|
|
1349
1391
|
console.log(` ${B}FLAGS${R}`);
|
|
1350
1392
|
blank();
|
|
1393
|
+
console.log(` ${CY}--demo${R} Run the live AI demo on built-in sample content`);
|
|
1351
1394
|
console.log(` ${CY}--json${R} Output raw JSON (great for CI pipelines)`);
|
|
1352
1395
|
console.log(` ${CY}--quiet${R} Output score number only (for scripting)`);
|
|
1353
1396
|
console.log(` ${CY}--save [file]${R} Save analysis to a markdown file (default: <input>.content-grade.md)`);
|
|
@@ -1493,20 +1536,25 @@ function cmdQuickDemo() {
|
|
|
1493
1536
|
blank();
|
|
1494
1537
|
hr();
|
|
1495
1538
|
if (isFirstRun()) {
|
|
1496
|
-
console.log(` ${B}${CY}
|
|
1539
|
+
console.log(` ${B}${CY}Try it on real content — 3 ways to start:${R}`);
|
|
1540
|
+
blank();
|
|
1541
|
+
console.log(` ${B}1.${R} Grade a headline ${D}(~5 seconds, works right now)${R}`);
|
|
1542
|
+
console.log(` ${CY}npx content-grade headline "Your headline here"${R}`);
|
|
1497
1543
|
blank();
|
|
1498
|
-
console.log(` ${
|
|
1499
|
-
console.log(`
|
|
1544
|
+
console.log(` ${B}2.${R} Run the live AI demo ${D}(full analysis on sample content)${R}`);
|
|
1545
|
+
console.log(` ${CY}npx content-grade --demo${R}`);
|
|
1500
1546
|
blank();
|
|
1501
|
-
console.log(` ${
|
|
1547
|
+
console.log(` ${B}3.${R} Analyze your own file`);
|
|
1548
|
+
console.log(` ${CY}npx content-grade analyze ./your-post.md${R}`);
|
|
1502
1549
|
blank();
|
|
1503
|
-
console.log(` ${D}
|
|
1504
|
-
console.log(` ${D}
|
|
1550
|
+
console.log(` ${D}All three require Claude CLI (free): ${CY}claude.ai/code${R}${D} → install → ${CY}claude login${R}`);
|
|
1551
|
+
console.log(` ${D}First time? Run: ${CY}npx content-grade init${R}${D} to verify setup.${R}`);
|
|
1505
1552
|
} else {
|
|
1506
|
-
console.log(` ${B}${CY}Grade
|
|
1553
|
+
console.log(` ${B}${CY}Grade your own content:${R}`);
|
|
1507
1554
|
blank();
|
|
1555
|
+
console.log(` ${CY}npx content-grade --demo${R} ${D}# live AI demo${R}`);
|
|
1508
1556
|
console.log(` ${CY}npx content-grade headline "Your headline here"${R} ${D}# ~5 seconds${R}`);
|
|
1509
|
-
console.log(` ${CY}npx content-grade analyze ./your-post.md${R} ${D}# full
|
|
1557
|
+
console.log(` ${CY}npx content-grade analyze ./your-post.md${R} ${D}# full analysis${R}`);
|
|
1510
1558
|
}
|
|
1511
1559
|
blank();
|
|
1512
1560
|
}
|
|
@@ -1864,7 +1912,7 @@ function analysisToMarkdown(result, sourceName, analyzedAt) {
|
|
|
1864
1912
|
}
|
|
1865
1913
|
|
|
1866
1914
|
lines.push(`---`);
|
|
1867
|
-
lines.push(`Generated by [ContentGrade](https://
|
|
1915
|
+
lines.push(`Generated by [ContentGrade](https://buy.stripe.com/5kQeVfew48dT7nf2W48k801) v${_version}`);
|
|
1868
1916
|
return lines.join('\n');
|
|
1869
1917
|
}
|
|
1870
1918
|
|
|
@@ -1884,8 +1932,9 @@ const _saveMode = _saveIdx !== -1;
|
|
|
1884
1932
|
const _savePath = (_saveMode && _rawArgs[_saveIdx + 1] && !_rawArgs[_saveIdx + 1].startsWith('-'))
|
|
1885
1933
|
? _rawArgs[_saveIdx + 1]
|
|
1886
1934
|
: null;
|
|
1935
|
+
const _demoMode = _rawArgs.includes('--demo');
|
|
1887
1936
|
const args = _rawArgs.filter((a, i) => {
|
|
1888
|
-
if (['--no-telemetry', '--json', '--quiet', '--verbose', '--ci', '--save'].includes(a)) return false;
|
|
1937
|
+
if (['--no-telemetry', '--json', '--quiet', '--verbose', '--ci', '--save', '--demo'].includes(a)) return false;
|
|
1889
1938
|
if (a === '--threshold') return false;
|
|
1890
1939
|
if (i > 0 && _rawArgs[i - 1] === '--threshold') return false;
|
|
1891
1940
|
if (_saveMode && i > 0 && _rawArgs[i - 1] === '--save' && !a.startsWith('-')) return false;
|
|
@@ -1924,6 +1973,7 @@ if (_rawArgs.includes('--help') || _rawArgs.includes('-h')) {
|
|
|
1924
1973
|
console.log(` ${CY}<file>${R} Path to a .md, .txt, or .mdx file`);
|
|
1925
1974
|
blank();
|
|
1926
1975
|
console.log(` ${B}Flags:${R}`);
|
|
1976
|
+
console.log(` ${CY}--demo${R} Run on built-in sample content (no file needed)`);
|
|
1927
1977
|
console.log(` ${CY}--json${R} Output raw JSON (great for CI pipelines)`);
|
|
1928
1978
|
console.log(` ${CY}--quiet${R} Output score number only (for scripting)`);
|
|
1929
1979
|
console.log(` ${CY}--ci${R} Exit 0 if score passes, 1 if it fails`);
|
|
@@ -1931,6 +1981,7 @@ if (_rawArgs.includes('--help') || _rawArgs.includes('-h')) {
|
|
|
1931
1981
|
console.log(` ${CY}--verbose${R} Show debug info: model, timing, response length`);
|
|
1932
1982
|
blank();
|
|
1933
1983
|
console.log(` ${B}Examples:${R}`);
|
|
1984
|
+
console.log(` ${CY}content-grade analyze --demo${R} ${D}# live demo on sample content${R}`);
|
|
1934
1985
|
console.log(` ${CY}content-grade analyze ./blog-post.md${R}`);
|
|
1935
1986
|
console.log(` ${CY}content-grade analyze ./readme.md --json${R}`);
|
|
1936
1987
|
console.log(` ${CY}content-grade analyze ./copy.md --ci --threshold 70${R}`);
|
|
@@ -2009,7 +2060,15 @@ if (_rawArgs.includes('--help') || _rawArgs.includes('-h')) {
|
|
|
2009
2060
|
}
|
|
2010
2061
|
}
|
|
2011
2062
|
|
|
2012
|
-
|
|
2063
|
+
if (_demoMode) {
|
|
2064
|
+
recordEvent({ event: 'command', command: 'demo' });
|
|
2065
|
+
cmdDemo().catch(err => {
|
|
2066
|
+
blank();
|
|
2067
|
+
fail(`Demo error: ${err.message}`);
|
|
2068
|
+
blank();
|
|
2069
|
+
process.exit(1);
|
|
2070
|
+
});
|
|
2071
|
+
} else switch (cmd) {
|
|
2013
2072
|
case 'analyze':
|
|
2014
2073
|
case 'analyse':
|
|
2015
2074
|
case 'check':
|
package/bin/telemetry.js
CHANGED
|
@@ -20,7 +20,7 @@ const EVENTS_FILE = join(CONFIG_DIR, 'events.jsonl');
|
|
|
20
20
|
// Telemetry endpoint: CLI events are forwarded here when telemetry is enabled.
|
|
21
21
|
// Set CONTENT_GRADE_TELEMETRY_URL to enable remote aggregation (e.g. self-hosted endpoint).
|
|
22
22
|
// Default: local-only (events stored at ~/.content-grade/events.jsonl, no remote send).
|
|
23
|
-
const REMOTE_URL = process.env.CONTENT_GRADE_TELEMETRY_URL || '';
|
|
23
|
+
const REMOTE_URL = process.env.CONTENT_GRADE_TELEMETRY_URL || 'https://cg-telemetry.onrender.com/api/telemetry/events';
|
|
24
24
|
|
|
25
25
|
// ── Internal helpers ──────────────────────────────────────────────────────────
|
|
26
26
|
|
|
@@ -72,15 +72,16 @@ export function initTelemetry() {
|
|
|
72
72
|
return { installId: cfg.installId, isNew: false, optedOut: false };
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
// First run — generate anonymous install ID, opt-
|
|
75
|
+
// First run — generate anonymous install ID, opt-in by default (like Next.js, Astro)
|
|
76
|
+
// Users can opt out: content-grade telemetry off, --no-telemetry, or CONTENT_GRADE_NO_TELEMETRY=1
|
|
76
77
|
const installId = generateId();
|
|
77
78
|
saveConfig({
|
|
78
79
|
installId,
|
|
79
80
|
installedAt: new Date().toISOString(),
|
|
80
|
-
telemetryEnabled:
|
|
81
|
+
telemetryEnabled: true,
|
|
81
82
|
});
|
|
82
83
|
|
|
83
|
-
return { installId, isNew: true, optedOut:
|
|
84
|
+
return { installId, isNew: true, optedOut: false };
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
/**
|
|
@@ -106,6 +107,7 @@ export function recordEvent(data) {
|
|
|
106
107
|
|
|
107
108
|
const event = {
|
|
108
109
|
...data,
|
|
110
|
+
package: 'content-grade',
|
|
109
111
|
installId,
|
|
110
112
|
timestamp: new Date().toISOString(),
|
|
111
113
|
};
|
package/package.json
CHANGED