@cloudcreate/adsense-check 1.4.0 → 1.4.2

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
@@ -176,7 +176,7 @@ adsense-check https://example.com --ai --api-key sk-xxx...
176
176
  │ ...
177
177
  └─ 评分: READY — 所有必要项达标
178
178
 
179
- ┌─ 柔性评分 ──────────────────────────────────── 75/100
179
+ ┌─ 智能评分 ──────────────────────────────────── 75/100
180
180
  │ ████████████████████ 100% 内容质量
181
181
  │ ████████████████████ 100% 用户体验
182
182
  │ ████████░░░░░░░░░░░░ 40% AI 内容分析
@@ -304,7 +304,7 @@ var en = {
304
304
  "report.content_label": "Content",
305
305
  // Two-group scoring
306
306
  "report.hard_requirements": "Hard Requirements",
307
- "report.soft_scoring": "Soft Scoring",
307
+ "report.soft_scoring": "Smart Scoring",
308
308
  "report.hard.ready": "READY \u2014 all requirements met",
309
309
  "report.hard.warn": "NEEDS FIXES \u2014 {count} warning(s) to address",
310
310
  "report.hard.fail": "NOT READY \u2014 {count} failure(s) must be fixed",
@@ -317,7 +317,7 @@ var en = {
317
317
  "group.policy": "Policy Compliance",
318
318
  "group.site_scale": "Site Scale",
319
319
  "group.content_quality": "Content Quality",
320
- "group.ai_value": "AI Value Analysis",
320
+ "group.ai_value": "Value Analysis",
321
321
  "group.ai_analysis": "AI Content Analysis",
322
322
  "group.page_quality": "Page Quality",
323
323
  "group.user_experience": "User Experience",
@@ -334,7 +334,7 @@ var en = {
334
334
  "reporter.topic": "Topic",
335
335
  "reporter.pages_label": "Pages",
336
336
  "reporter.confidence": "{confidence} confidence",
337
- "reporter.ai_value_label": "AI Value Score",
337
+ "reporter.ai_value_label": "Value Score",
338
338
  "reporter.ai_value_note": "geometric mean \xD7 page-type weights",
339
339
  "reporter.ai_dimensions": "AI Dimensions",
340
340
  "reporter.avg_per_10": "avg /10",
@@ -343,7 +343,8 @@ var en = {
343
343
  "reporter.dim_relevance": "Relevance",
344
344
  "reporter.dim_compliance": "Compliance",
345
345
  "reporter.formula_label": "Hard {hardPct}% \xD7 0.4 + Soft {softPct}% \xD7 0.6 - Penalty {penalty} = {total}",
346
- "reporter.mechanical_label": "mechanical",
346
+ "reporter.mechanical_label": "Base Score",
347
+ "reporter.advanced_label": "Advanced Score",
347
348
  // Markdown report
348
349
  "md.report_title": "AdSense Review Report",
349
350
  "md.table.project": "Item",
@@ -360,8 +361,8 @@ var en = {
360
361
  "md.table.confidence": "confidence",
361
362
  "md.composite_score_title": "Composite Score",
362
363
  "md.hard_requirements": "Hard Requirements",
363
- "md.soft_scoring": "Soft Scoring",
364
- "md.ai_value_title": "AI Value Analysis",
364
+ "md.soft_scoring": "Smart Scoring",
365
+ "md.ai_value_title": "Value Analysis",
365
366
  "md.table.dimension": "Dimension",
366
367
  "md.table.avg_score": "Avg Score",
367
368
  "md.dim_value": "Value",
@@ -377,7 +378,7 @@ var en = {
377
378
  "md.table.path": "Path",
378
379
  "md.table.score": "Score",
379
380
  "md.table.content_ratio": "Content Ratio",
380
- "md.table.ai_composite": "AI Composite",
381
+ "md.table.ai_composite": "Advanced",
381
382
  "md.table.title": "Title",
382
383
  "md.problem_pages": "Problem Page Details",
383
384
  "md.ai_status": "AI Status",
@@ -536,7 +537,7 @@ var zh = {
536
537
  "report.content_label": "\u6B63\u6587",
537
538
  // 两组评分
538
539
  "report.hard_requirements": "\u786C\u6027\u8981\u6C42",
539
- "report.soft_scoring": "\u67D4\u6027\u8BC4\u5206",
540
+ "report.soft_scoring": "\u667A\u80FD\u8BC4\u5206",
540
541
  "report.hard.ready": "READY \u2014 \u6240\u6709\u5FC5\u8981\u9879\u8FBE\u6807",
541
542
  "report.hard.warn": "NEEDS FIXES \u2014 {count} \u9879\u8B66\u544A\u5F85\u4FEE\u590D",
542
543
  "report.hard.fail": "NOT READY \u2014 {count} \u9879\u5931\u8D25\u5FC5\u987B\u4FEE\u590D",
@@ -549,7 +550,7 @@ var zh = {
549
550
  "group.policy": "\u653F\u7B56\u5408\u89C4",
550
551
  "group.site_scale": "\u7AD9\u70B9\u89C4\u6A21",
551
552
  "group.content_quality": "\u5185\u5BB9\u8D28\u91CF",
552
- "group.ai_value": "AI \u4EF7\u503C\u5206\u6790",
553
+ "group.ai_value": "\u4EF7\u503C\u5206\u6790",
553
554
  "group.ai_analysis": "AI \u5185\u5BB9\u5206\u6790",
554
555
  "group.page_quality": "\u9875\u9762\u8D28\u91CF",
555
556
  "group.user_experience": "\u7528\u6237\u4F53\u9A8C",
@@ -566,12 +567,13 @@ var zh = {
566
567
  "reporter.topic": "\u4E3B\u9898",
567
568
  "reporter.pages_label": "\u9875\u9762",
568
569
  "reporter.confidence": "\u7F6E\u4FE1\u5EA6: {confidence}",
569
- "reporter.ai_value_label": "AI \u4EF7\u503C\u8BC4\u5206",
570
+ "reporter.ai_value_label": "\u4EF7\u503C\u8BC4\u5206",
570
571
  "reporter.ai_value_note": "\u51E0\u4F55\u5747\u503C \xD7 \u9875\u9762\u7C7B\u578B\u52A0\u6743",
571
572
  "reporter.ai_dimensions": "AI \u7EF4\u5EA6",
572
573
  "reporter.avg_per_10": "\u5747\u5206 /10",
573
- "reporter.formula_label": "\u786C\u6027 {hardPct}% \xD7 0.4 + \u67D4\u6027 {softPct}% \xD7 0.6 - \u6263\u5206 {penalty} = {total}",
574
- "reporter.mechanical_label": "\u673A\u68B0\u8BC4\u5206",
574
+ "reporter.formula_label": "\u786C\u6027 {hardPct}% \xD7 0.4 + \u667A\u80FD {softPct}% \xD7 0.6 - \u6263\u5206 {penalty} = {total}",
575
+ "reporter.mechanical_label": "\u57FA\u7840\u8BC4\u5206",
576
+ "reporter.advanced_label": "\u9AD8\u7EA7\u8BC4\u5206",
575
577
  // 维度名称(终端和 Markdown)
576
578
  "reporter.dim_value": "\u4EF7\u503C",
577
579
  "reporter.dim_originality": "\u539F\u521B",
@@ -593,8 +595,8 @@ var zh = {
593
595
  "md.table.confidence": "\u7F6E\u4FE1\u5EA6",
594
596
  "md.composite_score_title": "\u7EFC\u5408\u8BC4\u5206",
595
597
  "md.hard_requirements": "\u786C\u6027\u8981\u6C42",
596
- "md.soft_scoring": "\u67D4\u6027\u8BC4\u5206",
597
- "md.ai_value_title": "AI \u4EF7\u503C\u5206\u6790",
598
+ "md.soft_scoring": "\u667A\u80FD\u8BC4\u5206",
599
+ "md.ai_value_title": "\u4EF7\u503C\u5206\u6790",
598
600
  "md.table.dimension": "\u7EF4\u5EA6",
599
601
  "md.table.avg_score": "\u5747\u5206",
600
602
  "md.dim_value": "\u4EF7\u503C",
@@ -610,7 +612,7 @@ var zh = {
610
612
  "md.table.path": "\u8DEF\u5F84",
611
613
  "md.table.score": "\u8BC4\u5206",
612
614
  "md.table.content_ratio": "\u6B63\u6587\u6BD4",
613
- "md.table.ai_composite": "AI \u7EFC\u5408",
615
+ "md.table.ai_composite": "\u9AD8\u7EA7\u8BC4\u5206",
614
616
  "md.table.title": "\u6807\u9898",
615
617
  "md.problem_pages": "\u95EE\u9898\u9875\u9762\u8BE6\u60C5",
616
618
  "md.ai_status": "AI \u72B6\u6001",
@@ -724,6 +726,13 @@ Also classify the page type based on its content and purpose. Choose ONE:
724
726
  - "required": About, Privacy, Terms, Contact, Editorial Policy, Legal
725
727
  - "utility": Search, Login, Signup, Download, 404, or functional tool pages
726
728
 
729
+ IMPORTANT \u2014 special handling for "required" and "utility" pages:
730
+ These pages are necessary for site operation. Do NOT penalize them for low value, originality, or relevance.
731
+ - For "required" pages (Privacy, Terms, About, Contact, Legal): set value=10, originality=10, relevance=10 automatically.
732
+ - Only score compliance normally. Check if the page has reasonable content (not empty or placeholder).
733
+ - For "utility" pages (Search, Login, 404): same rule \u2014 set value=10, originality=10, relevance=10, only evaluate compliance and basic completeness.
734
+ - For all other page types (homepage, listing, content, game_detail, video_detail, reference_detail): score all four dimensions normally.
735
+
727
736
  Page: ${page.url}
728
737
 
729
738
  Content:
@@ -747,17 +756,25 @@ Reply in ${langName} with JSON:
747
756
  const originalityScore = clampScore(result.originality);
748
757
  const relevanceScore = clampScore(result.relevance);
749
758
  const complianceScore = clampScore(result.compliance);
750
- const geoMean = Math.pow(valueScore * originalityScore * relevanceScore * complianceScore, 0.25);
751
- const status = geoMean >= 7 ? "pass" : geoMean >= 4 ? "warn" : "fail";
752
759
  const validPageTypes = ["homepage", "listing", "content", "game_detail", "video_detail", "reference_detail", "required", "utility"];
753
760
  const inferredPageType = validPageTypes.includes(result.pageType) ? result.pageType : void 0;
761
+ let finalValueScore = valueScore;
762
+ let finalOriginalityScore = originalityScore;
763
+ let finalRelevanceScore = relevanceScore;
764
+ if (inferredPageType === "required" || inferredPageType === "utility") {
765
+ finalValueScore = 10;
766
+ finalOriginalityScore = 10;
767
+ finalRelevanceScore = 10;
768
+ }
769
+ const geoMean = Math.pow(finalValueScore * finalOriginalityScore * finalRelevanceScore * complianceScore, 0.25);
770
+ const status = geoMean >= 7 ? "pass" : geoMean >= 4 ? "warn" : "fail";
754
771
  return {
755
772
  url: page.url,
756
773
  status,
757
- relevance: result.relevanceLabel ?? (relevanceScore >= 7 ? "relevant" : relevanceScore >= 4 ? "tangential" : "off-topic"),
758
- valueScore,
759
- originalityScore,
760
- relevanceScore,
774
+ relevance: result.relevanceLabel ?? (finalRelevanceScore >= 7 ? "relevant" : finalRelevanceScore >= 4 ? "tangential" : "off-topic"),
775
+ valueScore: finalValueScore,
776
+ originalityScore: finalOriginalityScore,
777
+ relevanceScore: finalRelevanceScore,
761
778
  complianceScore,
762
779
  assessment: result.assessment ?? "",
763
780
  suggestions: result.suggestions ?? [],
@@ -1835,7 +1852,12 @@ function buildPageDetails(pages, aiAnalyses, siteType) {
1835
1852
  const contentRatio = totalChars > 0 ? Math.round(contentChars / totalChars * 100) : 0;
1836
1853
  const issues = [];
1837
1854
  let contentStatus = "pass";
1838
- if (siteType === "content") {
1855
+ const ai = aiMap.get(page.url);
1856
+ const aiStatus = ai?.status;
1857
+ const relevance = ai?.relevance;
1858
+ const pageType = ai?.inferredPageType ?? classifyPage(page.url);
1859
+ const isFunctional = pageType === "required" || pageType === "utility";
1860
+ if (siteType === "content" && !isFunctional) {
1839
1861
  if (contentRatio < 30 && totalChars > 200) {
1840
1862
  issues.push(`Content ratio only ${contentRatio}%, mostly boilerplate`);
1841
1863
  contentStatus = "fail";
@@ -1845,10 +1867,6 @@ function buildPageDetails(pages, aiAnalyses, siteType) {
1845
1867
  contentStatus = contentStatus === "fail" ? "fail" : "warn";
1846
1868
  }
1847
1869
  }
1848
- const ai = aiMap.get(page.url);
1849
- const aiStatus = ai?.status;
1850
- const relevance = ai?.relevance;
1851
- const pageType = ai?.inferredPageType ?? classifyPage(page.url);
1852
1870
  const { score } = scorePage(pageType, contentChars, contentRatio, issues, siteType, aiStatus);
1853
1871
  const detail = { url: page.url, title: page.title, pageType, totalChars, contentChars, contentRatio, contentStatus, issues, score };
1854
1872
  if (relevance) detail.relevance = relevance;
@@ -2108,7 +2126,7 @@ async function check(options) {
2108
2126
  pageAnalyses = aiResult.pageAnalyses;
2109
2127
  const aiItems = [];
2110
2128
  if (aiResult.suggestions.length > 0) {
2111
- aiItems.push({ name: t("item.ai.suggestions", lang), status: "warn", message: t("ai.suggestion_count", lang, { count: aiResult.suggestions.length }), detail: aiResult.suggestions.join("; ") });
2129
+ aiItems.push({ name: t("item.ai.suggestions", lang), status: "warn", message: t("ai.suggestion_count", lang, { count: aiResult.suggestions.length }), detailList: aiResult.suggestions });
2112
2130
  }
2113
2131
  allCategories.push({ name: t("group.ai_value", lang), items: aiItems, group: "soft" });
2114
2132
  let suspiciousPages = pageAnalyses.filter((a) => {
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  getSupportedLangs,
11
11
  isValidLang,
12
12
  t
13
- } from "./chunk-XKNR4LB4.js";
13
+ } from "./chunk-MFO6RCM3.js";
14
14
 
15
15
  // src/cli.ts
16
16
  import "dotenv/config";
@@ -138,6 +138,9 @@ function renderTerminalReport(report) {
138
138
  for (const item of cat.items) {
139
139
  lines.push(` ${ICONS[item.status]} [${LABELS[item.status]}] ${item.message}`);
140
140
  if (item.detail) lines.push(chalk.gray(` ${item.detail}`));
141
+ if (item.detailList) {
142
+ for (const d of item.detailList) lines.push(chalk.gray(` \u2022 ${d}`));
143
+ }
141
144
  }
142
145
  lines.push("");
143
146
  }
@@ -145,8 +148,12 @@ function renderTerminalReport(report) {
145
148
  lines.push(chalk.bold(` ${t("report.page_details", lang)}`));
146
149
  lines.push(chalk.gray(` (${t("report.pages", lang, { count: report.pages.length })})`));
147
150
  lines.push("");
148
- const problems = report.pages.filter((p) => p.contentStatus !== "pass" || p.issues.length > 0 || p.ai && p.ai.status !== "pass");
149
- const ok = report.pages.filter((p) => p.contentStatus === "pass" && p.issues.length === 0 && (!p.ai || p.ai.status === "pass"));
151
+ const problems = report.pages.filter(
152
+ (p) => p.pageType !== "required" && p.pageType !== "utility" && (p.contentStatus !== "pass" || p.issues.length > 0 || p.ai && p.ai.status !== "pass")
153
+ );
154
+ const ok = report.pages.filter(
155
+ (p) => p.pageType === "required" || p.pageType === "utility" || p.contentStatus === "pass" && p.issues.length === 0 && (!p.ai || p.ai.status === "pass")
156
+ );
150
157
  for (const p of problems) renderPage(lines, p, lang);
151
158
  if (ok.length > 0) lines.push(chalk.gray(` ${t("report.pages_ok", lang, { count: ok.length })}`));
152
159
  lines.push("");
@@ -192,7 +199,9 @@ function renderPage(lines, page, lang) {
192
199
  const ratioColor = page.contentRatio >= 50 ? chalk.green : page.contentRatio >= 30 ? chalk.yellow : chalk.red;
193
200
  const scoreColor = page.score >= 80 ? chalk.green : page.score >= 50 ? chalk.yellow : chalk.red;
194
201
  const typeIcon = PAGE_TYPE_ICONS[page.pageType] || chalk.gray("?");
195
- lines.push(` ${ICONS[page.contentStatus]} ${typeIcon} ${chalk.bold(path)} ${scoreColor(page.score + "/100")}`);
202
+ const aiComposite = page.ai?.valueScore != null && page.ai?.originalityScore != null && page.ai?.relevanceScore != null && page.ai?.complianceScore != null ? Math.round(Math.pow(page.ai.valueScore * page.ai.originalityScore * page.ai.relevanceScore * page.ai.complianceScore, 0.25) * 10) : null;
203
+ const scoreLabels = aiComposite != null ? `${t("reporter.mechanical_label", lang)}: ${scoreColor(page.score + "/100")} | ${t("reporter.advanced_label", lang)}: ${aiComposite >= 70 ? chalk.green : aiComposite >= 40 ? chalk.yellow : chalk.red}(AI ${aiComposite}/100)` : `${t("reporter.mechanical_label", lang)}: ${scoreColor(page.score + "/100")}`;
204
+ lines.push(` ${ICONS[page.contentStatus]} ${typeIcon} ${chalk.bold(path)} ${scoreLabels}`);
196
205
  lines.push(chalk.gray(` ${page.title}`));
197
206
  lines.push(` ${t("report.content_label", lang)} ${ratioColor(page.contentRatio + "%")} (${page.contentChars}/${page.totalChars})`);
198
207
  for (const issue of page.issues) lines.push(chalk.yellow(` ! ${issue}`));
@@ -281,8 +290,12 @@ function renderMarkdownReport(report) {
281
290
  if (report.pages.length > 0) {
282
291
  lines.push(`### ${t("md.page_details", lang)} (${t("md.pages_count", lang, { count: report.pages.length })})`);
283
292
  lines.push("");
284
- const problems = report.pages.filter((p) => p.contentStatus !== "pass" || p.issues.length > 0 || p.ai && p.ai.status !== "pass");
285
- const ok = report.pages.filter((p) => p.contentStatus === "pass" && p.issues.length === 0 && (!p.ai || p.ai.status === "pass"));
293
+ const problems = report.pages.filter(
294
+ (p) => p.pageType !== "required" && p.pageType !== "utility" && (p.contentStatus !== "pass" || p.issues.length > 0 || p.ai && p.ai.status !== "pass")
295
+ );
296
+ const ok = report.pages.filter(
297
+ (p) => p.pageType === "required" || p.pageType === "utility" || p.contentStatus === "pass" && p.issues.length === 0 && (!p.ai || p.ai.status === "pass")
298
+ );
286
299
  lines.push(`| ${t("md.table.status", lang)} | ${t("md.table.type", lang)} | ${t("md.table.path", lang)} | ${t("md.table.score", lang)} | ${t("md.table.content_ratio", lang)} | V | O | R | C | ${t("md.table.ai_composite", lang)} | ${t("md.table.title", lang)} |`);
287
300
  lines.push(`|------|------|------|------|--------|---|---|---|---|--------|------|`);
288
301
  for (const p of [...problems, ...ok]) {
@@ -317,7 +330,9 @@ function renderMarkdownReport(report) {
317
330
  return p.url;
318
331
  }
319
332
  })();
320
- lines.push(`**[${path}](${p.url})** (${t("reporter.mechanical_label", lang)}: ${p.score}/100)`);
333
+ const aiComposite = p.ai?.valueScore != null && p.ai?.originalityScore != null && p.ai?.relevanceScore != null && p.ai?.complianceScore != null ? Math.round(Math.pow(p.ai.valueScore * p.ai.originalityScore * p.ai.relevanceScore * p.ai.complianceScore, 0.25) * 10) : null;
334
+ const scoreLabels = aiComposite != null ? `${t("reporter.mechanical_label", lang)}: ${p.score}/100 | ${t("reporter.advanced_label", lang)}: ${aiComposite}/100` : `${t("reporter.mechanical_label", lang)}: ${p.score}/100`;
335
+ lines.push(`**[${path}](${p.url})** (${scoreLabels})`);
321
336
  lines.push("");
322
337
  for (const issue of p.issues) lines.push(`- \u26A0\uFE0F ${issue}`);
323
338
  if (p.ai) {
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ interface CheckItem {
7
7
  status: CheckStatus;
8
8
  message: string;
9
9
  detail?: string;
10
+ detailList?: string[];
10
11
  }
11
12
  type CheckGroup = 'hard' | 'soft';
12
13
  interface CheckCategory {
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  check
3
- } from "./chunk-XKNR4LB4.js";
3
+ } from "./chunk-MFO6RCM3.js";
4
4
  export {
5
5
  check
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudcreate/adsense-check",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "Check if a website meets Google AdSense review requirements",
5
5
  "homepage": "https://cloudcreate.ai",
6
6
  "repository": {