geo-checker 0.3.1 → 0.3.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/dist/cli.cjs +150 -14
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +150 -14
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +142 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +142 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -305,10 +305,12 @@ async function buildContext(url, opts = {}) {
|
|
|
305
305
|
"Site appears to be JS-rendered (sparse body + SPA root element). Re-run with --render for accurate results."
|
|
306
306
|
);
|
|
307
307
|
}
|
|
308
|
-
const [robotsRaw, llmsRaw, llmsFullRaw] = await Promise.all([
|
|
308
|
+
const [robotsRaw, llmsRaw, llmsFullRaw, skillMdRaw, agentPermissionsRaw] = await Promise.all([
|
|
309
309
|
fetchText(`${origin}/robots.txt`, opts),
|
|
310
310
|
fetchText(`${origin}/llms.txt`, opts),
|
|
311
|
-
fetchText(`${origin}/llms-full.txt`, opts)
|
|
311
|
+
fetchText(`${origin}/llms-full.txt`, opts),
|
|
312
|
+
fetchText(`${origin}/skill.md`, opts),
|
|
313
|
+
fetchText(`${origin}/agent-permissions.json`, opts)
|
|
312
314
|
]);
|
|
313
315
|
let sitemapUrl = null;
|
|
314
316
|
const robots = robotsRaw ? parseRobots(robotsRaw) : null;
|
|
@@ -316,6 +318,13 @@ async function buildContext(url, opts = {}) {
|
|
|
316
318
|
if (!sitemapUrl) sitemapUrl = `${origin}/sitemap.xml`;
|
|
317
319
|
const sitemapRaw = await fetchText(sitemapUrl, opts);
|
|
318
320
|
const sitemap = sitemapRaw ? parseSitemap(sitemapRaw) : null;
|
|
321
|
+
let agentPermissions = null;
|
|
322
|
+
if (agentPermissionsRaw && agentPermissionsRaw.trim().length > 0) {
|
|
323
|
+
try {
|
|
324
|
+
agentPermissions = JSON.parse(agentPermissionsRaw);
|
|
325
|
+
} catch {
|
|
326
|
+
}
|
|
327
|
+
}
|
|
319
328
|
return {
|
|
320
329
|
url,
|
|
321
330
|
finalUrl,
|
|
@@ -330,7 +339,9 @@ async function buildContext(url, opts = {}) {
|
|
|
330
339
|
jsonLd: extractJsonLd($),
|
|
331
340
|
renderMode,
|
|
332
341
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
333
|
-
warnings
|
|
342
|
+
warnings,
|
|
343
|
+
skillMd: skillMdRaw && skillMdRaw.trim().length > 0 ? skillMdRaw : null,
|
|
344
|
+
agentPermissions
|
|
334
345
|
};
|
|
335
346
|
}
|
|
336
347
|
|
|
@@ -339,10 +350,11 @@ function defineRule(rule) {
|
|
|
339
350
|
return rule;
|
|
340
351
|
}
|
|
341
352
|
var CATEGORY_WEIGHTS = {
|
|
342
|
-
crawler:
|
|
343
|
-
"structured-data":
|
|
344
|
-
citation:
|
|
345
|
-
content:
|
|
353
|
+
crawler: 20,
|
|
354
|
+
"structured-data": 25,
|
|
355
|
+
citation: 20,
|
|
356
|
+
content: 15,
|
|
357
|
+
aeo: 20
|
|
346
358
|
};
|
|
347
359
|
|
|
348
360
|
// src/engine.ts
|
|
@@ -356,7 +368,8 @@ async function runRules(ctx, rules, opts = {}) {
|
|
|
356
368
|
crawler: { score: 0, weight: weights.crawler, results: [] },
|
|
357
369
|
"structured-data": { score: 0, weight: weights["structured-data"], results: [] },
|
|
358
370
|
citation: { score: 0, weight: weights.citation, results: [] },
|
|
359
|
-
content: { score: 0, weight: weights.content, results: [] }
|
|
371
|
+
content: { score: 0, weight: weights.content, results: [] },
|
|
372
|
+
aeo: { score: 0, weight: weights.aeo, results: [] }
|
|
360
373
|
};
|
|
361
374
|
for (const rule of rules) {
|
|
362
375
|
if (onlySet && !onlySet.has(rule.id) && (!rule.stableId || !onlySet.has(rule.stableId))) continue;
|
|
@@ -1946,12 +1959,130 @@ var contentRules = [
|
|
|
1946
1959
|
externalCitationsRule
|
|
1947
1960
|
];
|
|
1948
1961
|
|
|
1962
|
+
// src/rules/aeo/skill-md.ts
|
|
1963
|
+
var aeoSkillMdRule = defineRule({
|
|
1964
|
+
id: "aeo.skill-md",
|
|
1965
|
+
stableId: "aeo.skill-md",
|
|
1966
|
+
category: "aeo",
|
|
1967
|
+
group: "opportunity",
|
|
1968
|
+
weight: 3,
|
|
1969
|
+
impact: "high",
|
|
1970
|
+
effort: "low",
|
|
1971
|
+
docsUrl: "https://github.com/BaRam-OSS/geo-checker/blob/main/docs/rules.md#aeoskill-md",
|
|
1972
|
+
title: "skill.md is present",
|
|
1973
|
+
title_ko: "skill.md \uD30C\uC77C \uC874\uC7AC \uC5EC\uBD80",
|
|
1974
|
+
description: "A /skill.md file describes site capabilities so AI agents know what this site can do for them.",
|
|
1975
|
+
run(ctx) {
|
|
1976
|
+
if (ctx.skillMd !== null) {
|
|
1977
|
+
return {
|
|
1978
|
+
status: "pass",
|
|
1979
|
+
score: 1,
|
|
1980
|
+
rationale: "skill.md found at site root.",
|
|
1981
|
+
rationale_ko: "skill.md\uAC00 \uC0AC\uC774\uD2B8 \uB8E8\uD2B8\uC5D0 \uC874\uC7AC\uD569\uB2C8\uB2E4."
|
|
1982
|
+
};
|
|
1983
|
+
}
|
|
1984
|
+
return {
|
|
1985
|
+
status: "warn",
|
|
1986
|
+
score: 0,
|
|
1987
|
+
rationale: "No /skill.md found. Add one to describe your site capabilities to AI agents.",
|
|
1988
|
+
rationale_ko: "/skill.md\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. AI \uC5D0\uC774\uC804\uD2B8\uAC00 \uC0AC\uC774\uD2B8 \uAE30\uB2A5\uC744 \uD30C\uC545\uD560 \uC218 \uC788\uB3C4\uB85D \uCD94\uAC00\uD558\uC138\uC694.",
|
|
1989
|
+
fixHint: "Create /skill.md listing what services, products, and capabilities this site offers."
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1992
|
+
});
|
|
1993
|
+
|
|
1994
|
+
// src/rules/aeo/agent-permissions.ts
|
|
1995
|
+
var aeoAgentPermissionsRule = defineRule({
|
|
1996
|
+
id: "aeo.agent-permissions",
|
|
1997
|
+
stableId: "aeo.agent-permissions",
|
|
1998
|
+
category: "aeo",
|
|
1999
|
+
group: "opportunity",
|
|
2000
|
+
weight: 3,
|
|
2001
|
+
impact: "medium",
|
|
2002
|
+
effort: "low",
|
|
2003
|
+
docsUrl: "https://github.com/BaRam-OSS/geo-checker/blob/main/docs/rules.md#aeoagent-permissions",
|
|
2004
|
+
title: "agent-permissions.json is present",
|
|
2005
|
+
title_ko: "agent-permissions.json \uD30C\uC77C \uC874\uC7AC \uC5EC\uBD80",
|
|
2006
|
+
description: "Declares explicit read/summarize/cite/train permissions for AI agents.",
|
|
2007
|
+
run(ctx) {
|
|
2008
|
+
if (ctx.agentPermissions !== null) {
|
|
2009
|
+
return {
|
|
2010
|
+
status: "pass",
|
|
2011
|
+
score: 1,
|
|
2012
|
+
rationale: "agent-permissions.json found at site root.",
|
|
2013
|
+
rationale_ko: "agent-permissions.json\uC774 \uC0AC\uC774\uD2B8 \uB8E8\uD2B8\uC5D0 \uC874\uC7AC\uD569\uB2C8\uB2E4.",
|
|
2014
|
+
evidence: ctx.agentPermissions
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
return {
|
|
2018
|
+
status: "warn",
|
|
2019
|
+
score: 0,
|
|
2020
|
+
rationale: "No /agent-permissions.json found. Add one to declare AI agent access policy.",
|
|
2021
|
+
rationale_ko: "/agent-permissions.json\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. AI \uC5D0\uC774\uC804\uD2B8 \uC811\uADFC \uC815\uCC45\uC744 \uBA85\uC2DC\uD558\uB824\uBA74 \uCD94\uAC00\uD558\uC138\uC694.",
|
|
2022
|
+
fixHint: "Create /agent-permissions.json with read, summarize, cite, and train permission flags."
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2025
|
+
});
|
|
2026
|
+
|
|
2027
|
+
// src/rules/aeo/token-length.ts
|
|
2028
|
+
var THRESHOLD_OPTIMAL = 15e3;
|
|
2029
|
+
var THRESHOLD_MAX = 25e3;
|
|
2030
|
+
var aeoTokenLengthRule = defineRule({
|
|
2031
|
+
id: "aeo.token-length",
|
|
2032
|
+
stableId: "aeo.token-length",
|
|
2033
|
+
category: "aeo",
|
|
2034
|
+
group: "diagnostic",
|
|
2035
|
+
weight: 4,
|
|
2036
|
+
impact: "medium",
|
|
2037
|
+
effort: "medium",
|
|
2038
|
+
docsUrl: "https://github.com/BaRam-OSS/geo-checker/blob/main/docs/rules.md#aeotoken-length",
|
|
2039
|
+
title: "Content token length within AI agent limits",
|
|
2040
|
+
title_ko: "\uCF58\uD150\uCE20 \uD1A0\uD070 \uC218 AI \uC5D0\uC774\uC804\uD2B8 \uAD8C\uC7A5 \uBC94\uC704",
|
|
2041
|
+
description: "Pages under 15K tokens are optimal for AI agents (per Addy Osmani's AEO guidance).",
|
|
2042
|
+
run(ctx) {
|
|
2043
|
+
const text = ctx.$("body").text();
|
|
2044
|
+
const tokenEstimate = Math.round(text.length / 3);
|
|
2045
|
+
const evidence = { tokenEstimate, thresholds: { optimal: THRESHOLD_OPTIMAL, max: THRESHOLD_MAX } };
|
|
2046
|
+
if (tokenEstimate <= THRESHOLD_OPTIMAL) {
|
|
2047
|
+
return {
|
|
2048
|
+
status: "pass",
|
|
2049
|
+
score: 1,
|
|
2050
|
+
rationale: `Estimated ~${tokenEstimate.toLocaleString()} tokens \u2014 within optimal range.`,
|
|
2051
|
+
rationale_ko: `\uC608\uC0C1 \uD1A0\uD070 \uC218 ~${tokenEstimate.toLocaleString()} \u2014 \uAD8C\uC7A5 \uBC94\uC704(15K) \uC774\uB0B4\uC785\uB2C8\uB2E4.`,
|
|
2052
|
+
evidence
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2055
|
+
if (tokenEstimate <= THRESHOLD_MAX) {
|
|
2056
|
+
return {
|
|
2057
|
+
status: "warn",
|
|
2058
|
+
score: 0.5,
|
|
2059
|
+
rationale: `Estimated ~${tokenEstimate.toLocaleString()} tokens \u2014 exceeds 15K recommendation.`,
|
|
2060
|
+
rationale_ko: `\uC608\uC0C1 \uD1A0\uD070 \uC218 ~${tokenEstimate.toLocaleString()} \u2014 15K \uAD8C\uC7A5\uCE58\uB97C \uCD08\uACFC\uD569\uB2C8\uB2E4.`,
|
|
2061
|
+
fixHint: "Consider splitting content into shorter, focused pages.",
|
|
2062
|
+
evidence
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
return {
|
|
2066
|
+
status: "fail",
|
|
2067
|
+
score: 0,
|
|
2068
|
+
rationale: `Estimated ~${tokenEstimate.toLocaleString()} tokens \u2014 exceeds 25K agent processing limit.`,
|
|
2069
|
+
rationale_ko: `\uC608\uC0C1 \uD1A0\uD070 \uC218 ~${tokenEstimate.toLocaleString()} \u2014 AI \uC5D0\uC774\uC804\uD2B8 \uCC98\uB9AC \uD55C\uACC4(25K)\uB97C \uCD08\uACFC\uD569\uB2C8\uB2E4.`,
|
|
2070
|
+
fixHint: "Split this page into multiple focused pages under 15K tokens.",
|
|
2071
|
+
evidence
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
});
|
|
2075
|
+
|
|
2076
|
+
// src/rules/aeo/index.ts
|
|
2077
|
+
var aeoRules = [aeoSkillMdRule, aeoAgentPermissionsRule, aeoTokenLengthRule];
|
|
2078
|
+
|
|
1949
2079
|
// src/rules/index.ts
|
|
1950
2080
|
var defaultRules = [
|
|
1951
2081
|
...crawlerRules,
|
|
1952
2082
|
...structuredDataRules,
|
|
1953
2083
|
...citationRules,
|
|
1954
|
-
...contentRules
|
|
2084
|
+
...contentRules,
|
|
2085
|
+
...aeoRules
|
|
1955
2086
|
];
|
|
1956
2087
|
|
|
1957
2088
|
// src/config.ts
|
|
@@ -2147,7 +2278,8 @@ var CATEGORY_LABELS = {
|
|
|
2147
2278
|
crawler: "AI Crawler Access",
|
|
2148
2279
|
"structured-data": "Structured Data",
|
|
2149
2280
|
citation: "Citation Signals",
|
|
2150
|
-
content: "Content Structure"
|
|
2281
|
+
content: "Content Structure",
|
|
2282
|
+
aeo: "AEO Stack"
|
|
2151
2283
|
};
|
|
2152
2284
|
function scoreBadge(score) {
|
|
2153
2285
|
const color = score >= 85 ? "brightgreen" : score >= 60 ? "yellow" : "red";
|