content-grade 1.0.10 → 1.0.12

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
@@ -2,13 +2,14 @@
2
2
 
3
3
  > AI-powered content quality scoring for developers and content teams. Score any blog post, landing page, ad copy, or email — in under 30 seconds. No API keys. No accounts. Runs on your local Claude CLI.
4
4
 
5
+ [![CI](https://github.com/StanislavBG/Content-Grade/actions/workflows/ci.yml/badge.svg)](https://github.com/StanislavBG/Content-Grade/actions/workflows/ci.yml)
5
6
  [![npm](https://img.shields.io/npm/v/content-grade.svg)](https://www.npmjs.com/package/content-grade)
6
7
  [![npm downloads/month](https://img.shields.io/npm/dm/content-grade.svg)](https://www.npmjs.com/package/content-grade)
7
8
  [![npm downloads/week](https://img.shields.io/npm/dw/content-grade.svg)](https://www.npmjs.com/package/content-grade)
8
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
10
  [![Node.js 18+](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
10
11
  [![Requires Claude CLI](https://img.shields.io/badge/requires-Claude%20CLI-orange)](https://claude.ai/code)
11
- [![GitHub Discussions](https://img.shields.io/badge/GitHub-Discussions-blue?logo=github)](https://github.com/content-grade/Content-Grade/discussions)
12
+ [![GitHub Discussions](https://img.shields.io/badge/GitHub-Discussions-blue?logo=github)](https://github.com/StanislavBG/Content-Grade/discussions)
12
13
  [![Early Adopter seats open](https://img.shields.io/badge/Early%20Adopter-50%20seats%20open-success)](EARLY_ADOPTERS.md)
13
14
 
14
15
  ---
@@ -171,7 +172,7 @@ Launch with `content-grade start` — opens at [http://localhost:4000](http://lo
171
172
  | **EmailForge** | `/email-forge` | Subject line + body copy for click-through optimization |
172
173
  | **AudienceDecoder** | `/audience` | Twitter handle → audience archetypes and content patterns |
173
174
 
174
- Free tier: **50 analyses/day per tool**. Pro ($9/mo): **100 analyses/day** + all tools.
175
+ Free tier: **50 analyses/day per tool**. Pro ($19/mo): **100 analyses/day** + all tools.
175
176
 
176
177
  ---
177
178
 
@@ -515,7 +516,7 @@ Stripe variables are entirely optional — all tools work without them; upgrade
515
516
  ## Self-hosting / Development
516
517
 
517
518
  ```bash
518
- git clone https://github.com/content-grade/Content-Grade
519
+ git clone https://github.com/StanislavBG/Content-Grade
519
520
  cd Content-Grade
520
521
  npm install
521
522
  npm run dev # hot reload: server at :4000, client at :3000
@@ -912,7 +913,7 @@ echo "$DATE,$HEADLINE,$SCORE" >> headline-scores.csv
912
913
 
913
914
  | | Free | Pro |
914
915
  |-|------|-----|
915
- | Price | Free forever | $9/month |
916
+ | Price | Free forever | $19/month |
916
917
  | Analyses/day (CLI) | Unlimited | Unlimited |
917
918
  | Analyses/day (web dashboard, per tool) | 3 | 100 |
918
919
  | HeadlineGrader (single grade) | ✓ | ✓ |
@@ -945,25 +946,25 @@ ContentGrade is built in public. The community is the roadmap.
945
946
 
946
947
  ### GitHub Discussions
947
948
 
948
- **[Join the conversation →](https://github.com/content-grade/Content-Grade/discussions)**
949
+ **[Join the conversation →](https://github.com/StanislavBG/Content-Grade/discussions)**
949
950
 
950
951
  | Category | What it's for |
951
952
  |----------|--------------|
952
- | [Q&A](https://github.com/content-grade/Content-Grade/discussions/new?category=q-a) | "Why is my score lower than expected?" — questions get answered here |
953
- | [Show & Tell](https://github.com/content-grade/Content-Grade/discussions/new?category=show-and-tell) | Share your workflow, integration, or results — early adopters post here |
954
- | [Ideas](https://github.com/content-grade/Content-Grade/discussions/new?category=ideas) | Feature requests and suggestions before they become issues |
953
+ | [Q&A](https://github.com/StanislavBG/Content-Grade/discussions/new?category=q-a) | "Why is my score lower than expected?" — questions get answered here |
954
+ | [Show & Tell](https://github.com/StanislavBG/Content-Grade/discussions/new?category=show-and-tell) | Share your workflow, integration, or results — early adopters post here |
955
+ | [Ideas](https://github.com/StanislavBG/Content-Grade/discussions/new?category=ideas) | Feature requests and suggestions before they become issues |
955
956
 
956
957
  ### Early Adopter Program — 50 seats
957
958
 
958
959
  **[EARLY_ADOPTERS.md](EARLY_ADOPTERS.md)** — The first 50 users who install and share feedback get:
959
960
 
960
- - **Pro tier free for 12 months** ($9/mo value)
961
+ - **Pro tier free for 12 months** ($19/mo value)
961
962
  - Direct feedback channel with the maintainer
962
963
  - Name in CONTRIBUTORS.md (permanent)
963
964
  - Roadmap preview + design input before features ship
964
965
  - 30-minute onboarding call (optional)
965
966
 
966
- One action to claim: run ContentGrade, then post in [Show & Tell](https://github.com/content-grade/Content-Grade/discussions/new?category=show-and-tell) with `[Early Adopter]` in the title.
967
+ One action to claim: run ContentGrade, then post in [Show & Tell](https://github.com/StanislavBG/Content-Grade/discussions/new?category=show-and-tell) with `[Early Adopter]` in the title.
967
968
 
968
969
  ### Champions Program
969
970
 
@@ -975,16 +976,20 @@ See **[CONTRIBUTING.md](CONTRIBUTING.md)** for local dev setup, PR guidelines, a
975
976
 
976
977
  ### Roadmap
977
978
 
978
- **[ROADMAP.md](ROADMAP.md)** — what's built, what's next, what's planned, and what we're deliberately not doing. Open a [Discussion](https://github.com/content-grade/Content-Grade/discussions/new?category=ideas) to challenge or extend it.
979
+ **[ROADMAP.md](ROADMAP.md)** — what's built, what's next, what's planned, and what we're deliberately not doing. Open a [Discussion](https://github.com/StanislavBG/Content-Grade/discussions/new?category=ideas) to challenge or extend it.
979
980
 
980
981
  ### Feedback Loop
981
982
 
982
- **[FEEDBACK_LOOP.md](FEEDBACK_LOOP.md)** — how feedback flows into product decisions. Monthly structured check-ins, 48h triage SLA, and roadmap sync. Use the [monthly feedback template](https://github.com/content-grade/Content-Grade/issues/new?template=monthly_feedback.yml) to share your experience.
983
+ **[FEEDBACK_LOOP.md](FEEDBACK_LOOP.md)** — how feedback flows into product decisions. Monthly structured check-ins, 48h triage SLA, and roadmap sync. Use the [monthly feedback template](https://github.com/StanislavBG/Content-Grade/issues/new?template=monthly_feedback.yml) to share your experience.
983
984
 
984
985
  ### Contributors
985
986
 
986
987
  **[CONTRIBUTORS.md](CONTRIBUTORS.md)** — everyone who has improved ContentGrade. Code, bug reports, and real-world feedback all count. Early Adopters and Champions are listed here permanently.
987
988
 
989
+ ### How did you find ContentGrade?
990
+
991
+ If you found this tool useful, we'd love to know where you came across it — a Reddit thread, a blog post, a colleague's recommendation? Drop a note in [GitHub Discussions → Q&A](https://github.com/Content-Grade/Content-Grade/discussions/new?category=q-a) with the subject "How I found ContentGrade". This is 100% optional and takes 30 seconds. It helps us understand which communities to focus on and which content resonates.
992
+
988
993
  ### Use ContentGrade in your project
989
994
 
990
995
  Add a badge to show your content meets the bar:
@@ -999,6 +1004,11 @@ See [docs/social-proof/built-with-badge.md](docs/social-proof/built-with-badge.m
999
1004
 
1000
1005
  ---
1001
1006
 
1007
+ ## Legal
1008
+
1009
+ - [Privacy Policy](https://content-grade.github.io/Content-Grade/privacy.html)
1010
+ - [Terms of Service](https://content-grade.github.io/Content-Grade/terms.html)
1011
+
1002
1012
  ## License
1003
1013
 
1004
1014
  MIT
@@ -388,8 +388,16 @@ async function cmdAnalyze(filePath) {
388
388
  result = parseJSON(raw);
389
389
  recordEvent({ event: 'analyze_result', score: result.total_score, content_type: result.content_type });
390
390
  // Machine-readable output modes (exit cleanly, skip styled output)
391
- if (_jsonMode) { process.stdout.write(JSON.stringify(result, null, 2) + '\n'); return; }
392
- if (_quietMode) { process.stdout.write(`${result.total_score}\n`); return; }
391
+ if (_jsonMode) {
392
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
393
+ if (_ciMode) process.exit(result.total_score >= _ciThreshold ? 0 : 1);
394
+ return;
395
+ }
396
+ if (_quietMode) {
397
+ process.stdout.write(`${result.total_score}\n`);
398
+ if (_ciMode) process.exit(result.total_score >= _ciThreshold ? 0 : 1);
399
+ return;
400
+ }
393
401
  } catch (err) {
394
402
  process.stdout.write(`\n`);
395
403
  blank();
@@ -498,6 +506,21 @@ async function cmdAnalyze(filePath) {
498
506
  blank();
499
507
  console.log(` ${MG}Upgrade to Pro ($9/mo) for unlimited analyses →${R} ${CY}https://content-grade.github.io/Content-Grade/${R}`);
500
508
  }
509
+
510
+ // CI exit code — shown after full output so user sees the score before exit
511
+ if (_ciMode) {
512
+ const passed = result.total_score >= _ciThreshold;
513
+ blank();
514
+ hr();
515
+ if (passed) {
516
+ ok(`CI PASS — score ${result.total_score} meets threshold ${_ciThreshold}`);
517
+ } else {
518
+ fail(`CI FAIL — score ${result.total_score} is below threshold ${_ciThreshold}`);
519
+ }
520
+ blank();
521
+ process.exit(passed ? 0 : 1);
522
+ }
523
+
501
524
  blank();
502
525
  }
503
526
 
@@ -1187,7 +1210,32 @@ async function cmdMetrics() {
1187
1210
  }
1188
1211
  blank();
1189
1212
 
1190
- // 2. Local telemetry summary
1213
+ // 2. GitHub repository stats (public API, no auth)
1214
+ console.log(` ${B}GitHub${R}`);
1215
+ const ghData = await new Promise((resolve) => {
1216
+ const req = httpsGet(
1217
+ 'https://api.github.com/repos/StanislavBG/Content-Grade',
1218
+ { headers: { 'User-Agent': `content-grade-cli/${_version}` }, timeout: 5000 },
1219
+ (res) => {
1220
+ let data = '';
1221
+ res.on('data', (c) => data += c);
1222
+ res.on('end', () => { try { resolve(JSON.parse(data)); } catch { resolve(null); } });
1223
+ }
1224
+ );
1225
+ req.on('error', () => resolve(null));
1226
+ req.on('timeout', () => { req.destroy(); resolve(null); });
1227
+ });
1228
+ if (ghData?.stargazers_count != null) {
1229
+ console.log(` ${D}Stars: ${R}${B}${ghData.stargazers_count.toLocaleString()}${R}`);
1230
+ console.log(` ${D}Forks: ${R}${B}${ghData.forks_count.toLocaleString()}${R}`);
1231
+ console.log(` ${D}Open issues: ${R}${B}${ghData.open_issues_count.toLocaleString()}${R}`);
1232
+ console.log(` ${D}Watchers: ${R}${B}${ghData.watchers_count.toLocaleString()}${R}`);
1233
+ } else {
1234
+ console.log(` ${D}Unavailable (offline or rate limited)${R}`);
1235
+ }
1236
+ blank();
1237
+
1238
+ // 3. Local telemetry summary
1191
1239
  console.log(` ${B}Local Usage${R}`);
1192
1240
  const eventsFile = join(homedir(), '.content-grade', 'events.jsonl');
1193
1241
  try {
@@ -1245,6 +1293,8 @@ function cmdHelp() {
1245
1293
  blank();
1246
1294
  console.log(` ${CY}batch <directory>${R} ${MG}[Pro]${R} Analyze all .md/.txt files in a directory`);
1247
1295
  blank();
1296
+ console.log(` ${CY}seo-audit <url>${R} Fetch a live URL and analyze its content (SEO, readability, structure)`);
1297
+ blank();
1248
1298
  console.log(` ${CY}headline "<text>"${R} Grade a single headline (4 copywriting frameworks)`);
1249
1299
  blank();
1250
1300
  console.log(` ${CY}activate${R} Enter license key to unlock Pro features`);
@@ -1266,6 +1316,8 @@ function cmdHelp() {
1266
1316
  blank();
1267
1317
  console.log(` ${CY}--json${R} Output raw JSON (great for CI pipelines)`);
1268
1318
  console.log(` ${CY}--quiet${R} Output score number only (for scripting)`);
1319
+ console.log(` ${CY}--ci${R} Exit 0 if score passes threshold, exit 1 if it fails`);
1320
+ console.log(` ${CY}--threshold <n>${R} Score threshold for --ci mode (default: 60)`);
1269
1321
  console.log(` ${CY}--verbose${R} Show debug info: model, timing, raw response length`);
1270
1322
  console.log(` ${CY}--version${R} Print version and exit`);
1271
1323
  console.log(` ${CY}--no-telemetry${R} Skip usage tracking for this invocation`);
@@ -1279,8 +1331,11 @@ function cmdHelp() {
1279
1331
  console.log(` ${D}# Grade README.md (alias)${R}`);
1280
1332
  console.log(` ${CY}content-grade grade README.md${R}`);
1281
1333
  blank();
1282
- console.log(` ${D}# CI pipeline exit 1 if score < threshold${R}`);
1283
- console.log(` ${CY}content-grade analyze ./blog.md --json | jq '.total_score'${R}`);
1334
+ console.log(` ${D}# Audit a live URL${R}`);
1335
+ console.log(` ${CY}content-grade seo-audit https://yoursite.com/blog-post${R}`);
1336
+ blank();
1337
+ console.log(` ${D}# CI pipeline — fail build if score is below 70${R}`);
1338
+ console.log(` ${CY}content-grade analyze ./blog.md --ci --threshold 70${R}`);
1284
1339
  blank();
1285
1340
  console.log(` ${D}# Get score number only (for scripts)${R}`);
1286
1341
  console.log(` ${CY}content-grade analyze ./post.md --quiet${R}`);
@@ -1470,7 +1525,8 @@ async function cmdDemo() {
1470
1525
 
1471
1526
  // Show telemetry notice after user has seen value (first run only)
1472
1527
  if (_showTelemNotice) {
1473
- console.log(` ${D}Anonymous usage data is collected by default. Opt out: ${CY}content-grade telemetry off${R}`);
1528
+ console.log(` ${D}Help improve ContentGrade enable anonymous usage analytics: ${CY}content-grade telemetry on${R}`);
1529
+ console.log(` ${D}No PII, no file contents. View what's collected: ${CY}content-grade telemetry${R}`);
1474
1530
  blank();
1475
1531
  }
1476
1532
 
@@ -1696,7 +1752,17 @@ const _rawArgs = process.argv.slice(2);
1696
1752
  const _jsonMode = _rawArgs.includes('--json');
1697
1753
  const _quietMode = _rawArgs.includes('--quiet');
1698
1754
  const _verboseMode = _rawArgs.includes('--verbose') || _rawArgs.includes('-v') && !_rawArgs.includes('--version');
1699
- const args = _rawArgs.filter(a => !['--no-telemetry', '--json', '--quiet', '--verbose'].includes(a));
1755
+ const _ciMode = _rawArgs.includes('--ci');
1756
+ const _threshIdx = _rawArgs.indexOf('--threshold');
1757
+ const _ciThreshold = (_threshIdx !== -1 && _rawArgs[_threshIdx + 1])
1758
+ ? (parseInt(_rawArgs[_threshIdx + 1], 10) || 60)
1759
+ : 60;
1760
+ const args = _rawArgs.filter((a, i) => {
1761
+ if (['--no-telemetry', '--json', '--quiet', '--verbose', '--ci'].includes(a)) return false;
1762
+ if (a === '--threshold') return false;
1763
+ if (i > 0 && _rawArgs[i - 1] === '--threshold') return false;
1764
+ return true;
1765
+ });
1700
1766
  const raw = args[0];
1701
1767
  const cmd = raw?.toLowerCase();
1702
1768
 
@@ -1775,6 +1841,26 @@ switch (cmd) {
1775
1841
  });
1776
1842
  break;
1777
1843
 
1844
+ case 'seo-audit':
1845
+ case 'seoaudit':
1846
+ recordEvent({ event: 'command', command: 'seo-audit' });
1847
+ if (!args[1]) {
1848
+ blank();
1849
+ fail(`No URL specified.`);
1850
+ blank();
1851
+ console.log(` ${B}Usage:${R} ${CY}content-grade seo-audit <url>${R}`);
1852
+ console.log(` ${B}Example:${R} ${CY}content-grade seo-audit https://yoursite.com/blog-post${R}`);
1853
+ blank();
1854
+ process.exit(2);
1855
+ }
1856
+ cmdAnalyzeUrl(args[1]).catch(err => {
1857
+ blank();
1858
+ fail(`Unexpected error: ${err.message}`);
1859
+ blank();
1860
+ process.exit(1);
1861
+ });
1862
+ break;
1863
+
1778
1864
  case 'batch':
1779
1865
  recordEvent({ event: 'command', command: 'batch' });
1780
1866
  cmdBatch(args[1]).catch(err => {
package/bin/telemetry.js CHANGED
@@ -72,18 +72,15 @@ export function initTelemetry() {
72
72
  return { installId: cfg.installId, isNew: false, optedOut: false };
73
73
  }
74
74
 
75
- // First run — generate anonymous install ID
75
+ // First run — generate anonymous install ID, opt-IN by default (off until user enables)
76
76
  const installId = generateId();
77
77
  saveConfig({
78
78
  installId,
79
79
  installedAt: new Date().toISOString(),
80
- telemetryEnabled: true,
80
+ telemetryEnabled: false,
81
81
  });
82
82
 
83
- // Record install event
84
- _write({ event: 'install', installId, platform: process.platform, nodeVersion: process.version });
85
-
86
- return { installId, isNew: true, optedOut: false };
83
+ return { installId, isNew: true, optedOut: true };
87
84
  }
88
85
 
89
86
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "content-grade",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
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": {