content-grade 1.0.9 → 1.0.11
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 +1 -1
- package/bin/content-grade.js +100 -5
- package/bin/telemetry.js +3 -6
- package/package.json +1 -1
package/CONTRIBUTING.md
CHANGED
|
@@ -16,7 +16,7 @@ Thanks for wanting to contribute. ContentGrade is a CLI + web dashboard for AI-p
|
|
|
16
16
|
## Local development setup
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
git clone https://github.com/
|
|
19
|
+
git clone https://github.com/Content-Grade/Content-Grade
|
|
20
20
|
cd Content-Grade
|
|
21
21
|
pnpm install
|
|
22
22
|
pnpm dev
|
package/bin/content-grade.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import { execFileSync, execFile, spawn } from 'child_process';
|
|
17
17
|
import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync, statSync } from 'fs';
|
|
18
|
-
import { resolve, dirname, basename, extname } from 'path';
|
|
18
|
+
import { resolve, dirname, basename, extname, join } from 'path';
|
|
19
19
|
import { homedir } from 'os';
|
|
20
20
|
import { fileURLToPath } from 'url';
|
|
21
21
|
import { promisify } from 'util';
|
|
@@ -113,7 +113,7 @@ function blank() { console.log(''); }
|
|
|
113
113
|
function banner() {
|
|
114
114
|
blank();
|
|
115
115
|
console.log(`${B}${CY} ╔═══════════════════════════════════════╗${R}`);
|
|
116
|
-
console.log(`${B}${CY} ║ ContentGrade ·
|
|
116
|
+
console.log(`${B}${CY} ║ ContentGrade · AI content scoring ║${R}`);
|
|
117
117
|
console.log(`${B}${CY} ╚═══════════════════════════════════════╝${R}`);
|
|
118
118
|
blank();
|
|
119
119
|
}
|
|
@@ -1150,16 +1150,104 @@ async function cmdCheckUpdates() {
|
|
|
1150
1150
|
blank();
|
|
1151
1151
|
}
|
|
1152
1152
|
|
|
1153
|
+
// ── Metrics ───────────────────────────────────────────────────────────────────
|
|
1154
|
+
|
|
1155
|
+
async function cmdMetrics() {
|
|
1156
|
+
blank();
|
|
1157
|
+
console.log(` ${B}ContentGrade Metrics${R}`);
|
|
1158
|
+
blank();
|
|
1159
|
+
|
|
1160
|
+
// 1. npm download stats (public API, no auth)
|
|
1161
|
+
console.log(` ${B}npm Downloads${R}`);
|
|
1162
|
+
const fetchNpm = (period) => new Promise((resolve) => {
|
|
1163
|
+
const url = `https://api.npmjs.org/downloads/point/${period}/content-grade`;
|
|
1164
|
+
const mod = url.startsWith('https') ? httpsGet : httpGet;
|
|
1165
|
+
const req = mod(url, { timeout: 5000 }, (res) => {
|
|
1166
|
+
let data = '';
|
|
1167
|
+
res.on('data', (c) => data += c);
|
|
1168
|
+
res.on('end', () => {
|
|
1169
|
+
try { resolve(JSON.parse(data)); } catch { resolve(null); }
|
|
1170
|
+
});
|
|
1171
|
+
});
|
|
1172
|
+
req.on('error', () => resolve(null));
|
|
1173
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
const [week, month] = await Promise.all([
|
|
1177
|
+
fetchNpm('last-week'),
|
|
1178
|
+
fetchNpm('last-month'),
|
|
1179
|
+
]);
|
|
1180
|
+
if (week?.downloads != null) {
|
|
1181
|
+
console.log(` ${D}Last 7 days: ${R}${B}${week.downloads.toLocaleString()}${R} installs`);
|
|
1182
|
+
} else {
|
|
1183
|
+
console.log(` ${D}Last 7 days: ${R}${D}unavailable (offline?)${R}`);
|
|
1184
|
+
}
|
|
1185
|
+
if (month?.downloads != null) {
|
|
1186
|
+
console.log(` ${D}Last 30 days: ${R}${B}${month.downloads.toLocaleString()}${R} installs`);
|
|
1187
|
+
}
|
|
1188
|
+
blank();
|
|
1189
|
+
|
|
1190
|
+
// 2. GitHub repository stats (public API, no auth)
|
|
1191
|
+
console.log(` ${B}GitHub${R}`);
|
|
1192
|
+
const ghData = await new Promise((resolve) => {
|
|
1193
|
+
const req = httpsGet(
|
|
1194
|
+
'https://api.github.com/repos/StanislavBG/Content-Grade',
|
|
1195
|
+
{ headers: { 'User-Agent': `content-grade-cli/${_version}` }, timeout: 5000 },
|
|
1196
|
+
(res) => {
|
|
1197
|
+
let data = '';
|
|
1198
|
+
res.on('data', (c) => data += c);
|
|
1199
|
+
res.on('end', () => { try { resolve(JSON.parse(data)); } catch { resolve(null); } });
|
|
1200
|
+
}
|
|
1201
|
+
);
|
|
1202
|
+
req.on('error', () => resolve(null));
|
|
1203
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
1204
|
+
});
|
|
1205
|
+
if (ghData?.stargazers_count != null) {
|
|
1206
|
+
console.log(` ${D}Stars: ${R}${B}${ghData.stargazers_count.toLocaleString()}${R}`);
|
|
1207
|
+
console.log(` ${D}Forks: ${R}${B}${ghData.forks_count.toLocaleString()}${R}`);
|
|
1208
|
+
console.log(` ${D}Open issues: ${R}${B}${ghData.open_issues_count.toLocaleString()}${R}`);
|
|
1209
|
+
console.log(` ${D}Watchers: ${R}${B}${ghData.watchers_count.toLocaleString()}${R}`);
|
|
1210
|
+
} else {
|
|
1211
|
+
console.log(` ${D}Unavailable (offline or rate limited)${R}`);
|
|
1212
|
+
}
|
|
1213
|
+
blank();
|
|
1214
|
+
|
|
1215
|
+
// 3. Local telemetry summary
|
|
1216
|
+
console.log(` ${B}Local Usage${R}`);
|
|
1217
|
+
const eventsFile = join(homedir(), '.content-grade', 'events.jsonl');
|
|
1218
|
+
try {
|
|
1219
|
+
const lines = readFileSync(eventsFile, 'utf8').trim().split('\n').filter(Boolean);
|
|
1220
|
+
const events = lines.map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
|
|
1221
|
+
const commands = events.filter(e => e.event === 'command');
|
|
1222
|
+
const installs = events.filter(e => e.event === 'install');
|
|
1223
|
+
const byCmd = {};
|
|
1224
|
+
for (const e of commands) { byCmd[e.command] = (byCmd[e.command] || 0) + 1; }
|
|
1225
|
+
console.log(` ${D}Total runs: ${R}${B}${commands.length}${R}`);
|
|
1226
|
+
console.log(` ${D}Installs: ${R}${B}${installs.length}${R}`);
|
|
1227
|
+
if (Object.keys(byCmd).length) {
|
|
1228
|
+
const top = Object.entries(byCmd).sort((a, b) => b[1] - a[1]).slice(0, 5);
|
|
1229
|
+
console.log(` ${D}Top commands: ${R}${top.map(([k, v]) => `${CY}${k}${R}(${v})`).join(' ')}`);
|
|
1230
|
+
}
|
|
1231
|
+
} catch {
|
|
1232
|
+
console.log(` ${D}No local events yet${R}`);
|
|
1233
|
+
}
|
|
1234
|
+
blank();
|
|
1235
|
+
|
|
1236
|
+
console.log(` ${D}Full install history: ${CY}https://npmjs.com/package/content-grade${R}`);
|
|
1237
|
+
blank();
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1153
1240
|
// ── Help ──────────────────────────────────────────────────────────────────────
|
|
1154
1241
|
|
|
1155
1242
|
function cmdHelp() {
|
|
1156
1243
|
banner();
|
|
1244
|
+
console.log(` ${D}AI-powered content quality scoring — readability, SEO, structure analysis${R}`);
|
|
1157
1245
|
console.log(` ${D}v${_version} · ${CY}content-grade.github.io/Content-Grade${R}`);
|
|
1158
1246
|
blank();
|
|
1159
1247
|
console.log(` ${B}QUICK START${R}`);
|
|
1160
1248
|
blank();
|
|
1161
|
-
console.log(` ${CY}npx content-grade headline "Your
|
|
1162
|
-
console.log(` ${CY}npx content-grade analyze ./post.md${R}
|
|
1249
|
+
console.log(` ${CY}npx content-grade headline "Your Headline Here"${R} ${D}# grade a headline (fastest, ~5s)${R}`);
|
|
1250
|
+
console.log(` ${CY}npx content-grade analyze ./my-post.md${R} ${D}# full AI content analysis (~20s)${R}`);
|
|
1163
1251
|
console.log(` ${CY}npx content-grade demo${R} ${D}# live demo on sample content${R}`);
|
|
1164
1252
|
console.log(` ${CY}npx content-grade start${R} ${D}# launch web dashboard (6 tools)${R}`);
|
|
1165
1253
|
blank();
|
|
@@ -1191,6 +1279,8 @@ function cmdHelp() {
|
|
|
1191
1279
|
blank();
|
|
1192
1280
|
console.log(` ${CY}init${R} Run if Claude CLI isn't detected or tools aren't working`);
|
|
1193
1281
|
blank();
|
|
1282
|
+
console.log(` ${CY}metrics${R} Show npm download counts and local usage stats`);
|
|
1283
|
+
blank();
|
|
1194
1284
|
console.log(` ${CY}telemetry [on|off]${R} View or toggle anonymous usage tracking`);
|
|
1195
1285
|
blank();
|
|
1196
1286
|
console.log(` ${CY}check-updates${R} Check if a newer version is available on npm`);
|
|
@@ -1405,7 +1495,8 @@ async function cmdDemo() {
|
|
|
1405
1495
|
|
|
1406
1496
|
// Show telemetry notice after user has seen value (first run only)
|
|
1407
1497
|
if (_showTelemNotice) {
|
|
1408
|
-
console.log(` ${D}
|
|
1498
|
+
console.log(` ${D}Help improve ContentGrade — enable anonymous usage analytics: ${CY}content-grade telemetry on${R}`);
|
|
1499
|
+
console.log(` ${D}No PII, no file contents. View what's collected: ${CY}content-grade telemetry${R}`);
|
|
1409
1500
|
blank();
|
|
1410
1501
|
}
|
|
1411
1502
|
|
|
@@ -1745,6 +1836,10 @@ switch (cmd) {
|
|
|
1745
1836
|
console.log(`content-grade v${_version}`);
|
|
1746
1837
|
break;
|
|
1747
1838
|
|
|
1839
|
+
case 'metrics':
|
|
1840
|
+
cmdMetrics().catch(() => {});
|
|
1841
|
+
break;
|
|
1842
|
+
|
|
1748
1843
|
case 'telemetry': {
|
|
1749
1844
|
const sub = args[1];
|
|
1750
1845
|
if (sub === 'off') {
|
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:
|
|
80
|
+
telemetryEnabled: false,
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
-
|
|
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