katman-mcp 0.1.0
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/LICENSE +13 -0
- package/README.md +160 -0
- package/dist/cli.js +71 -0
- package/dist/context.js +43 -0
- package/dist/crawlers.js +86 -0
- package/dist/index.js +25 -0
- package/dist/license.js +448 -0
- package/dist/meta.js +28 -0
- package/dist/net.js +128 -0
- package/dist/server.js +17 -0
- package/dist/state.js +81 -0
- package/dist/tools/account/account.js +64 -0
- package/dist/tools/account/cli.js +21 -0
- package/dist/tools/account/index.js +48 -0
- package/dist/tools/account/sync.js +142 -0
- package/dist/tools/account/upsell.js +290 -0
- package/dist/tools/crawl/audit/analyze.js +719 -0
- package/dist/tools/crawl/audit/bots.js +114 -0
- package/dist/tools/crawl/audit/checks.js +126 -0
- package/dist/tools/crawl/audit/html.js +382 -0
- package/dist/tools/crawl/audit/index.js +364 -0
- package/dist/tools/crawl/audit/model.js +1 -0
- package/dist/tools/crawl/audit/report.js +199 -0
- package/dist/tools/crawl/audit/robots.js +199 -0
- package/dist/tools/crawl/audit/sitemap.js +147 -0
- package/dist/tools/crawl/cli.js +106 -0
- package/dist/tools/crawl/common/advice.js +61 -0
- package/dist/tools/crawl/common/findings.js +57 -0
- package/dist/tools/crawl/common/routes.js +80 -0
- package/dist/tools/crawl/common/text.js +89 -0
- package/dist/tools/crawl/common/urls.js +64 -0
- package/dist/tools/crawl/index.js +226 -0
- package/dist/tools/crawl/indexnow.js +271 -0
- package/dist/tools/crawl/keywords.js +178 -0
- package/dist/tools/crawl/scan/framework.js +645 -0
- package/dist/tools/crawl/scan/index.js +175 -0
- package/dist/tools/crawl/scan/project.js +180 -0
- package/dist/tools/crawl/scan/routes.js +354 -0
- package/dist/tools/crawl/scan/signals.js +414 -0
- package/dist/tools/method/cli.js +42 -0
- package/dist/tools/method/core/brief.js +302 -0
- package/dist/tools/method/core/extract.js +435 -0
- package/dist/tools/method/core/generate.js +504 -0
- package/dist/tools/method/core/lint.js +556 -0
- package/dist/tools/method/core/plan.js +405 -0
- package/dist/tools/method/core/prompt.js +150 -0
- package/dist/tools/method/core/questions.js +196 -0
- package/dist/tools/method/core/report.js +182 -0
- package/dist/tools/method/core/research-run.js +148 -0
- package/dist/tools/method/core/research.js +448 -0
- package/dist/tools/method/core/snippets.js +292 -0
- package/dist/tools/method/core/start.js +277 -0
- package/dist/tools/method/data/anatomy.js +141 -0
- package/dist/tools/method/data/appendices.js +202 -0
- package/dist/tools/method/data/cloudflare.js +192 -0
- package/dist/tools/method/data/evidence.js +137 -0
- package/dist/tools/method/data/geo-jobs.js +128 -0
- package/dist/tools/method/data/index.js +19 -0
- package/dist/tools/method/data/landing.js +67 -0
- package/dist/tools/method/data/lessons.js +127 -0
- package/dist/tools/method/data/measurement.js +142 -0
- package/dist/tools/method/data/method.js +141 -0
- package/dist/tools/method/data/offsite.js +148 -0
- package/dist/tools/method/data/pages.js +167 -0
- package/dist/tools/method/data/prompts.js +633 -0
- package/dist/tools/method/data/publish.js +113 -0
- package/dist/tools/method/data/render.js +117 -0
- package/dist/tools/method/data/research.js +131 -0
- package/dist/tools/method/data/roadmap.js +105 -0
- package/dist/tools/method/data/traps.js +74 -0
- package/dist/tools/method/data/verify.js +88 -0
- package/dist/tools/method/data/viral-list.js +39 -0
- package/dist/tools/method/index.js +209 -0
- package/dist/tools/method/lib/calls.js +53 -0
- package/dist/tools/method/lib/framework.js +525 -0
- package/dist/tools/method/lib/l10n.js +17 -0
- package/dist/tools/method/lib/routes.js +118 -0
- package/dist/tools/method/lib/state.js +454 -0
- package/dist/tools/method/lib/text.js +165 -0
- package/dist/tools/method/mcp/prompts.js +79 -0
- package/dist/tools/method/mcp/resources.js +50 -0
- package/dist/tools/visibility/analyze.js +463 -0
- package/dist/tools/visibility/cli.js +86 -0
- package/dist/tools/visibility/estimate.js +105 -0
- package/dist/tools/visibility/index.js +108 -0
- package/dist/tools/visibility/judge.js +156 -0
- package/dist/tools/visibility/manual.js +275 -0
- package/dist/tools/visibility/models.js +277 -0
- package/dist/tools/visibility/monitoring.js +165 -0
- package/dist/tools/visibility/openrouter.js +449 -0
- package/dist/tools/visibility/render.js +295 -0
- package/dist/tools/visibility/run.js +684 -0
- package/dist/tools/visibility/summary.js +164 -0
- package/dist/types.js +3 -0
- package/dist/util.js +92 -0
- package/package.json +54 -0
- package/server.json +34 -0
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
/** Placeholders the prompt bodies may contain; katman_prompt fills them from research/config/scan. */
|
|
2
|
+
export const PLACEHOLDERS = [
|
|
3
|
+
"brand", "domain", "site_url", "category", "sentence", "not_x", "situation", "situation_url", "competitors",
|
|
4
|
+
"languages", "app_paths",
|
|
5
|
+
];
|
|
6
|
+
export const PROMPTS = [
|
|
7
|
+
{
|
|
8
|
+
id: "P0",
|
|
9
|
+
tag: { en: "EVERY SESSION", tr: "HER OTURUM" },
|
|
10
|
+
title: { en: "Context: at the start of every session", tr: "Bağlam: her oturumun başında" },
|
|
11
|
+
when: {
|
|
12
|
+
en: "At the start of every coding session on SEO/GEO work, before any code changes.",
|
|
13
|
+
tr: "SEO/GEO üzerinde çalışılan her oturumun başında, kod değişikliğinden önce.",
|
|
14
|
+
},
|
|
15
|
+
body: {
|
|
16
|
+
tr: `Bu projede SEO (Google, Bing) ve GEO (ChatGPT, Claude, Gemini, Perplexity gibi asistanlarda görünürlük) üzerinde çalışacağız.
|
|
17
|
+
Önce .katman/research.md ve .katman/plan.md dosyalarını oku: müşteri durumları, markanın tek cümlesi, "X değildir" notu ve rakipler research.md'de; sayfa planı plan.md'de.
|
|
18
|
+
Kurallar:
|
|
19
|
+
- research.md'de kaynağı olmayan hiçbir sayı, müşteri yorumu, rakip bilgisi ya da iddia ekleme. Bilmediğin yere [DOLDUR] yaz.
|
|
20
|
+
- Sayfada görünmeyen hiçbir bilgiyi schema'ya, meta açıklamaya ya da llms.txt'ye yazma.
|
|
21
|
+
- Giriş gerektiren uygulama ekranlarına dokunma; yalnızca herkese açık sayfalar.
|
|
22
|
+
- Her değişiklikten sonra hangi dosyada neyi değiştirdiğini liste hâlinde yaz.`,
|
|
23
|
+
en: `In this project we will work on SEO (Google, Bing) and GEO (visibility in assistants such as ChatGPT, Claude, Gemini and Perplexity).
|
|
24
|
+
First read .katman/research.md and .katman/plan.md: the customer situations, the brand's one sentence, the "not-X" note and the competitors are in research.md; the page plan is in plan.md.
|
|
25
|
+
Rules:
|
|
26
|
+
- Don't add any number, customer review, competitor fact or claim that has no source in research.md. Where you don't know, write [FILL].
|
|
27
|
+
- Never put information into schema, meta descriptions or llms.txt that isn't visible on the page.
|
|
28
|
+
- Don't touch app screens that require login; public pages only.
|
|
29
|
+
- After every change, list which file you changed and what you changed.`,
|
|
30
|
+
},
|
|
31
|
+
accept: {
|
|
32
|
+
en: "The assistant writes no code until it has summarised research.md and you have approved the page plan.",
|
|
33
|
+
tr: "Asistan research.md’yi özetleyip sayfa planını senden onaylatmadan kod yazmıyor.",
|
|
34
|
+
},
|
|
35
|
+
why: {
|
|
36
|
+
en: "An AI coding tool starts from zero every session; if the rules aren't written down, it makes things up.",
|
|
37
|
+
tr: "Yapay zekâ kod aracı her oturumda sıfırdan başlar; kurallar yazılı değilse uydurur.",
|
|
38
|
+
},
|
|
39
|
+
verifiedBy: [
|
|
40
|
+
{ tool: "katman_research", check: { en: "mode \"show\": research is complete enough to summarise (completeness %, no [FILL] in what the page needs)", tr: "mode \"show\": araştırma özetlenecek kadar dolu (tamamlanma yüzdesi, sayfanın ihtiyaç duyduğu yerde [DOLDUR] yok)" } },
|
|
41
|
+
{ tool: "manual", check: { en: "you approved the page plan in .katman/plan.md before any code change", tr: "kod değişikliğinden önce .katman/plan.md’deki sayfa planını onayladınız" } },
|
|
42
|
+
],
|
|
43
|
+
gates: [],
|
|
44
|
+
week: 1,
|
|
45
|
+
changesCode: false,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "P1",
|
|
49
|
+
tag: { en: "DAY ONE", tr: "İLK GÜN" },
|
|
50
|
+
title: { en: "Audit: a report without changing code", tr: "Denetim: kod değiştirmeden rapor" },
|
|
51
|
+
when: {
|
|
52
|
+
en: "On day one, and on every site you take over: an audit that changes nothing.",
|
|
53
|
+
tr: "İlk gün ve devraldığınız her sitede: hiçbir şeyi değiştirmeyen denetim.",
|
|
54
|
+
},
|
|
55
|
+
body: {
|
|
56
|
+
tr: `Kodu değiştirme, yalnızca rapor ver. Yayındaki ({site_url}) her herkese açık sayfa için bir tablo çıkar:
|
|
57
|
+
URL | sayfa sunucuda HTML olarak mı üretiliyor, tarayıcıda JavaScript ile mi (SSR / SSG / CSR) | <title> ve uzunluğu | meta description ve uzunluğu | canonical | robots meta | H1 sayısı | alt metni olmayan görsel | og:image | JSON-LD türleri | bu sayfaya link veren sayfa sayısı.
|
|
58
|
+
Ayrıca şunları yanıtla:
|
|
59
|
+
1. robots.txt ve sitemap.xml var mı, nasıl üretiliyor, sitemap'te hangi adresler var?
|
|
60
|
+
2. Bulunmayan bir adres (ör. /olmayan-sayfa) 404 durum kodu mu dönüyor, 200 mü?
|
|
61
|
+
3. Menü ve linkler <a href> mi, yoksa onClick ile mi yönlendiriyor?
|
|
62
|
+
4. Aynı site başka bir adreste de yayında mı (önizleme, staging, *.vercel.app gibi)?
|
|
63
|
+
Sonunda en önemli 10 sorunu, trafiğe etkisine göre sırala. Her biri için hangi dosyanın değişeceğini yaz.`,
|
|
64
|
+
en: `Don't change any code; report only. For every public page on the live site ({site_url}), produce a table:
|
|
65
|
+
URL | is the page rendered as HTML on the server or with JavaScript in the browser (SSR / SSG / CSR) | <title> and its length | meta description and its length | canonical | robots meta | number of H1s | images without alt text | og:image | JSON-LD types | number of pages linking to this page.
|
|
66
|
+
Also answer:
|
|
67
|
+
1. Do robots.txt and sitemap.xml exist, how are they generated, which URLs are in the sitemap?
|
|
68
|
+
2. Does a URL that doesn't exist (e.g. /this-page-does-not-exist) return a 404 status code or 200?
|
|
69
|
+
3. Are the menu and the links <a href> elements, or do they navigate with onClick?
|
|
70
|
+
4. Is the same site also live at another address (preview, staging, *.vercel.app and similar)?
|
|
71
|
+
Finally, rank the 10 most important problems by their impact on traffic. For each one, say which file will change.`,
|
|
72
|
+
},
|
|
73
|
+
accept: {
|
|
74
|
+
en: "Every public page is in the table; there is a clear answer on at least 404s, rendering and orphan pages.",
|
|
75
|
+
tr: "Her herkese açık sayfa tabloda; en az 404, render şekli ve yetim sayfalar konusunda net bir cevap var.",
|
|
76
|
+
},
|
|
77
|
+
why: {
|
|
78
|
+
en: "Most of the viral list's 20 items show up in this one report. Start fixing from the report.",
|
|
79
|
+
tr: "Listenin 20 maddesinin çoğu bu tek raporla görünür. Düzeltmeye rapordan başlayın.",
|
|
80
|
+
},
|
|
81
|
+
katmanNote: {
|
|
82
|
+
en: "Katman produces most of this table for you: katman_scan_code (rendering verdict, routes, onClick links, preview hosts) and katman_audit_site (per-URL checks, soft 404, orphan pages, AI bot access). Build the report from their output.",
|
|
83
|
+
tr: "Bu tablonun çoğunu Katman üretir: katman_scan_code (render şekli, yollar, onClick linkler, önizleme adresleri) ve katman_audit_site (adres bazında kontroller, sahte 404, yetim sayfalar, yapay zekâ botu erişimi). Raporu onların çıktısından kurun.",
|
|
84
|
+
},
|
|
85
|
+
verifiedBy: [
|
|
86
|
+
{ tool: "katman_scan_code", check: { en: "rendering verdict (SSR/SSG/CSR), public vs app routes, onClick navigation, preview-URL patterns", tr: "render kararı (SSR/SSG/CSR), herkese açık ve uygulama yolları, onClick yönlendirme, önizleme adresleri" } },
|
|
87
|
+
{ tool: "katman_audit_site", check: { en: "one row per public URL; soft-404 test; pages with 0 internal links in (orphans)", tr: "herkese açık her adres için bir satır; sahte 404 testi; hiç iç link almayan (yetim) sayfalar" } },
|
|
88
|
+
],
|
|
89
|
+
gates: ["read", "quote", "fit"],
|
|
90
|
+
week: 1,
|
|
91
|
+
changesCode: false,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "P2",
|
|
95
|
+
tag: { en: "IF SPA, DO THIS FIRST", tr: "SPA İSE ÖNCE BU" },
|
|
96
|
+
title: { en: "Move the text into the HTML (don't hide it in JavaScript)", tr: "Metni HTML’e taşı (JavaScript’e saklama)" },
|
|
97
|
+
when: {
|
|
98
|
+
en: "First, if the site is a single-page app (React/Vite, older Lovable, Bolt, Replit, CRA) or the audit shows few words with JavaScript off.",
|
|
99
|
+
tr: "Site tek sayfa uygulamaysa (React/Vite, eski Lovable, Bolt, Replit, CRA) ya da denetim JavaScript kapalıyken az kelime gösteriyorsa, önce bu.",
|
|
100
|
+
},
|
|
101
|
+
body: {
|
|
102
|
+
tr: `Sayfaların metni JavaScript çalışmadan da HTML'de görünmeli: başlıklar, paragraflar, fiyatlar, SSS cevapları ve linkler.
|
|
103
|
+
Mevcut çatıya göre en küçük değişikliği seç ve neden seçtiğini yaz: sunucu tarafı render (SSR), derleme anında statik üretim (SSG) ya da herkese açık sayfalar için ön-render.
|
|
104
|
+
Uygulama içi ekranları olduğu gibi bırak. Yönlendirmede # parçası değil, gerçek yollar (History API) kullan.
|
|
105
|
+
Bittiğinde her sayfa için şu komutun çıktısında H1'in ve ilk paragrafın metin olarak göründüğünü göster:
|
|
106
|
+
curl -s {site_url}/SAYFA | sed 's/<[^>]*>/ /g' | tr -s ' ' | head -c 600`,
|
|
107
|
+
en: `Page text must be in the HTML even without JavaScript: headings, paragraphs, prices, FAQ answers and links.
|
|
108
|
+
Pick the smallest change for the current framework and say why you chose it: server-side rendering (SSR), static generation at build time (SSG), or prerendering the public pages.
|
|
109
|
+
Leave the in-app screens as they are. For routing, use real paths (History API), not # fragments.
|
|
110
|
+
When you're done, show for every page that the H1 and the first paragraph appear as text in the output of:
|
|
111
|
+
curl -s {site_url}/PAGE | sed 's/<[^>]*>/ /g' | tr -s ' ' | head -c 600`,
|
|
112
|
+
},
|
|
113
|
+
accept: {
|
|
114
|
+
en: "With JavaScript off, every page's H1 and first paragraph can be read.",
|
|
115
|
+
tr: "JavaScript kapalıyken her sayfanın H1’i ve ilk paragrafı okunuyor.",
|
|
116
|
+
},
|
|
117
|
+
why: {
|
|
118
|
+
en: "Google runs JavaScript later but still recommends server rendering; per our research notes, ChatGPT's search bot doesn't run JavaScript.",
|
|
119
|
+
tr: "Google JavaScript’i sonradan çalıştırıyor ama yine de sunucuda render’ı öneriyor; araştırma notumuza göre ChatGPT’nin arama botu JavaScript çalıştırmıyor.",
|
|
120
|
+
},
|
|
121
|
+
katmanNote: {
|
|
122
|
+
en: "Strongest evidence of all GEO tactics: across 500M+ logged fetches, no major AI crawler ran JavaScript. Prerender the same content users see; serving bots different or richer content is cloaking (Google spam policy, which since May 2026 also covers manipulating AI answers).",
|
|
123
|
+
tr: "Bütün GEO taktikleri içinde kanıtı en güçlü olan bu: 500 milyonu aşan kayıtlı istekte hiçbir büyük yapay zekâ tarayıcısı JavaScript çalıştırmadı. Kullanıcının gördüğü içeriğin aynısını ön-render edin; bota farklı ya da daha zengin içerik göstermek gizlemedir (cloaking; Google’ın spam politikası Mayıs 2026’dan beri yapay zekâ cevaplarını manipüle etmeyi de kapsıyor).",
|
|
124
|
+
},
|
|
125
|
+
verifiedBy: [
|
|
126
|
+
{ tool: "katman_audit_site", check: { en: "pages[].wordsJsOff (words in the raw HTML) and pages[].firstParagraph are present for every public page", tr: "her herkese açık sayfada pages[].wordsJsOff (ham HTML’deki kelime) ve pages[].firstParagraph dolu" } },
|
|
127
|
+
{ tool: "katman_scan_code", check: { en: "rendering.verdict is ssr, ssg, hybrid or static-html for the public routes", tr: "herkese açık yollar için rendering.verdict ssr, ssg, hybrid ya da static-html" } },
|
|
128
|
+
],
|
|
129
|
+
gates: ["read"],
|
|
130
|
+
week: 2,
|
|
131
|
+
changesCode: true,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "P3",
|
|
135
|
+
title: { en: "Head tags from one source", tr: "Head etiketleri tek kaynaktan" },
|
|
136
|
+
when: {
|
|
137
|
+
en: "Week 2, when titles or descriptions are missing, duplicated, too long, or written in scattered files.",
|
|
138
|
+
tr: "2. hafta: title ya da açıklamalar eksik, tekrarlı, fazla uzun ya da dağınık dosyalarda yazılıysa.",
|
|
139
|
+
},
|
|
140
|
+
body: {
|
|
141
|
+
tr: `Her sayfanın başlık, açıklama ve paylaşım bilgilerini tek bir dosyadan üret (ör. src/seo/pages.ts). Her sayfada:
|
|
142
|
+
- benzersiz <title>: önce müşterinin durumu, sonra marka ({brand}); yaklaşık 60 karakter
|
|
143
|
+
- benzersiz meta description: research.md'deki "tek cümle" kalıbıyla ("{sentence}"), yaklaşık 150-160 karakter
|
|
144
|
+
- kendi tam adresini gösteren canonical (https, {domain} alan adıyla)
|
|
145
|
+
- <html lang="..."> sayfa hangi dildeyse (sitenin dilleri: {languages})
|
|
146
|
+
- og:title, og:description, og:url, og:image (1200x630, tam adres), og:image:alt, twitter:card = summary_large_image
|
|
147
|
+
- çok dilliyse her dil için hreflang, kendisi dahil, ve x-default
|
|
148
|
+
og:image için markanın renkleriyle 1200x630 bir kart tasarla ve /img/og/ altına koy.
|
|
149
|
+
Sonunda iki sayfanın aynı title ya da description'ı paylaşmadığını kontrol eden bir test yaz.`,
|
|
150
|
+
en: `Generate every page's title, description and sharing tags from a single file (e.g. src/seo/pages.ts). On every page:
|
|
151
|
+
- a unique <title>: the customer's situation first, then the brand ({brand}); about 60 characters
|
|
152
|
+
- a unique meta description following the "one sentence" pattern in research.md ("{sentence}"); about 150–160 characters
|
|
153
|
+
- a canonical pointing to the page's own full URL (https, on {domain})
|
|
154
|
+
- <html lang="..."> set to the page's language (site languages: {languages})
|
|
155
|
+
- og:title, og:description, og:url, og:image (1200x630, absolute URL), og:image:alt, twitter:card = summary_large_image
|
|
156
|
+
- if the site is multilingual: hreflang for every language, including the page itself, plus x-default
|
|
157
|
+
For og:image, design a 1200x630 card in the brand's colours and put it under /img/og/.
|
|
158
|
+
Finally, write a test that checks no two pages share a title or a description.`,
|
|
159
|
+
},
|
|
160
|
+
accept: {
|
|
161
|
+
en: "The test passes; every page's head comes from one file; the og:image URL opens in a browser.",
|
|
162
|
+
tr: "Test geçiyor; her sayfanın head’i tek dosyadan geliyor; og:image adresi tarayıcıda açılıyor.",
|
|
163
|
+
},
|
|
164
|
+
why: {
|
|
165
|
+
en: "Titles and descriptions written in scattered files get copied and go stale. The sentence an assistant repeats also comes from this description.",
|
|
166
|
+
tr: "Başlık ve açıklama dağınık dosyalarda yazılınca kopyalanır ve eskir. Asistanın söylediği cümle de bu açıklamadan gelir.",
|
|
167
|
+
},
|
|
168
|
+
verifiedBy: [
|
|
169
|
+
{ tool: "katman_audit_site", check: { en: "title and description present, unique and within length; canonicalSelf; ogImage; lang; reciprocal hreflang", tr: "title ve açıklama var, benzersiz, uzunluk sınırında; canonical kendine; og:image; lang; karşılıklı hreflang" } },
|
|
170
|
+
{ tool: "katman_scan_code", check: { en: "files.headSource points to one head/SEO source", tr: "files.headSource tek bir head/SEO kaynağını gösteriyor" } },
|
|
171
|
+
],
|
|
172
|
+
gates: ["quote"],
|
|
173
|
+
week: 2,
|
|
174
|
+
changesCode: true,
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
id: "P4",
|
|
178
|
+
title: { en: "robots.txt and sitemap.xml", tr: "robots.txt ve sitemap.xml" },
|
|
179
|
+
when: {
|
|
180
|
+
en: "Week 2, when robots.txt or sitemap.xml is missing, blocks bots, or lists pages that shouldn't be indexed.",
|
|
181
|
+
tr: "2. hafta: robots.txt ya da sitemap.xml yoksa, botları engelliyorsa ya da dizine girmemesi gereken sayfaları listeliyorsa.",
|
|
182
|
+
},
|
|
183
|
+
body: {
|
|
184
|
+
tr: `Kökte robots.txt oluştur:
|
|
185
|
+
- Arama motorlarına ve yapay zekâ botlarına adıyla izin ver: Googlebot, Bingbot, OAI-SearchBot, ChatGPT-User, GPTBot, ClaudeBot, Claude-User, Claude-SearchBot, PerplexityBot, Perplexity-User.
|
|
186
|
+
- Yalnızca uygulama yollarını kapat (ör. {app_paths}).
|
|
187
|
+
- En alta: Sitemap: {site_url}/sitemap.xml
|
|
188
|
+
sitemap.xml'i sayfa listesinden derleme anında üret: yalnızca 200 dönen, canonical ve indekslenmesini istediğimiz adresler. lastmod yalnızca sayfanın metni değiştiğinde değişsin (her derlemede bugünün tarihi değil).
|
|
189
|
+
Giriş, kayıt, ödeme, teşekkür ve site içi arama sayfalarını sitemap'e koyma; bunlara <meta name="robots" content="noindex,follow"> ekle.
|
|
190
|
+
Dikkat: noindex eklediğin bir sayfayı robots.txt'de kapatma; kapatırsan Google noindex'i göremez.
|
|
191
|
+
Dikkat: çıplak önek yazma (Disallow: /t hem /tr'yi hem /tools'u kapatır); Content-Signal gibi standart dışı satırları yorum olarak yaz. Örnek dosya: Ek A (katman_generate robots.txt bu projeye göre üretir).`,
|
|
192
|
+
en: `Create robots.txt at the root:
|
|
193
|
+
- Allow search engines and AI bots by name: Googlebot, Bingbot, OAI-SearchBot, ChatGPT-User, GPTBot, ClaudeBot, Claude-User, Claude-SearchBot, PerplexityBot, Perplexity-User.
|
|
194
|
+
- Close only the app paths (e.g. {app_paths}).
|
|
195
|
+
- At the bottom: Sitemap: {site_url}/sitemap.xml
|
|
196
|
+
Generate sitemap.xml from the page list at build time: only URLs that return 200, are canonical and that we want indexed. lastmod changes only when the page's text changes (not today's date on every build).
|
|
197
|
+
Keep login, sign-up, checkout, thank-you and on-site search pages out of the sitemap; give them <meta name="robots" content="noindex,follow">.
|
|
198
|
+
Careful: don't close a page you gave noindex in robots.txt; if you do, Google can't see the noindex.
|
|
199
|
+
Careful: never write bare prefixes (Disallow: /t closes both /tr and /tools); write non-standard lines such as Content-Signal as comments. Sample file: appendix A (katman_generate robots.txt builds it for this project).`,
|
|
200
|
+
},
|
|
201
|
+
accept: {
|
|
202
|
+
en: "robots.txt has no `Disallow: /`; every URL in the sitemap returns 200 and is canonical; no noindex page is in the sitemap.",
|
|
203
|
+
tr: "robots.txt’de Disallow: / yok; sitemap’teki her adres 200 dönüyor ve canonical; noindex’li sayfa sitemap’te değil.",
|
|
204
|
+
},
|
|
205
|
+
why: {
|
|
206
|
+
en: "robots.txt manages crawling, not the index. One wrong line closes the whole site; a missing permission leaves the assistant bots outside.",
|
|
207
|
+
tr: "robots.txt taramayı yönetir, dizini değil. Yanlış bir satır bütün siteyi kapatır; eksik bir izin asistan botlarını dışarıda bırakır.",
|
|
208
|
+
},
|
|
209
|
+
katmanNote: {
|
|
210
|
+
en: "robots.txt is a request; the CDN decides at the door. After changing robots.txt, check that AI bots get 200 through your CDN/firewall (book ch. 6).",
|
|
211
|
+
tr: "robots.txt bir ricadır; kapıdaki kararı CDN verir. robots.txt’yi değiştirdikten sonra yapay zekâ botlarının CDN ya da güvenlik duvarından 200 aldığını kontrol edin (06. bölüm).",
|
|
212
|
+
},
|
|
213
|
+
verifiedBy: [
|
|
214
|
+
{ tool: "katman_audit_site", check: { en: "site.robots: blocksAll false, no blockedAiTokens, no invalidLines or bare prefixes; sitemap URLs all 200, canonical, not noindex", tr: "site.robots: blocksAll false, blockedAiTokens boş, geçersiz satır ya da çıplak önek yok; sitemap adreslerinin hepsi 200, canonical, noindex değil" } },
|
|
215
|
+
{ tool: "katman_generate", check: { en: "artifact robots.txt gives the reference file for this project", tr: "robots.txt çıktısı bu projeye göre örnek dosyayı verir" } },
|
|
216
|
+
],
|
|
217
|
+
gates: ["read"],
|
|
218
|
+
week: 2,
|
|
219
|
+
changesCode: true,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: "P5",
|
|
223
|
+
title: { en: "Schema (JSON-LD)", tr: "Schema (JSON-LD)" },
|
|
224
|
+
when: {
|
|
225
|
+
en: "Week 2, after P3: sitewide Organization and WebSite, plus the right type on each page.",
|
|
226
|
+
tr: "2. hafta, P3’ten sonra: bütün sitede Organization ve WebSite, her sayfada doğru tip.",
|
|
227
|
+
},
|
|
228
|
+
body: {
|
|
229
|
+
tr: `Sayfalara JSON-LD ekle, yalnızca sayfada görünen bilgiyle:
|
|
230
|
+
- Tüm sitede: Organization (name, url, logo, sameAs: sosyal hesaplar; markanız başka bir şeyle karışıyorsa alternateName ve disambiguatingDescription ile "X değildir": "{not_x}") ve WebSite.
|
|
231
|
+
- Ürün / hizmet sayfası: Service ya da SoftwareApplication; fiyat sayfadaki ile birebir aynı.
|
|
232
|
+
- Yazılar ve rehberler: Article (headline, datePublished, dateModified, author).
|
|
233
|
+
- Alt sayfalar: BreadcrumbList. SSS bölümü olan sayfa: FAQPage.
|
|
234
|
+
JSON-LD'yi sayfa içeriğiyle aynı veriden üret; elle ikinci kez yazma.
|
|
235
|
+
Bittiğinde her sayfanın JSON-LD bloğunun geçerli JSON olduğunu doğrulayan bir test ekle.`,
|
|
236
|
+
en: `Add JSON-LD to the pages, using only information visible on the page:
|
|
237
|
+
- Sitewide: Organization (name, url, logo, sameAs: social accounts; if your brand gets confused with something, add alternateName and a "not-X" disambiguatingDescription: "{not_x}") and WebSite.
|
|
238
|
+
- Product / service page: Service or SoftwareApplication; the price exactly as on the page.
|
|
239
|
+
- Posts and guides: Article (headline, datePublished, dateModified, author).
|
|
240
|
+
- Sub-pages: BreadcrumbList. A page with an FAQ section: FAQPage.
|
|
241
|
+
Generate the JSON-LD from the same data as the page content; never write it by hand a second time.
|
|
242
|
+
When you're done, add a test that checks every page's JSON-LD block is valid JSON.`,
|
|
243
|
+
},
|
|
244
|
+
accept: {
|
|
245
|
+
en: "Google's Rich Results Test shows no errors; every price and claim in the schema appears word for word on the page.",
|
|
246
|
+
tr: "Google Zengin Sonuçlar Testi’nde hata yok; schema’daki her fiyat ve iddia sayfada aynen yazıyor.",
|
|
247
|
+
},
|
|
248
|
+
why: {
|
|
249
|
+
en: "Schema doesn't guarantee rich results; the FAQ rich result was retired in May 2026. Its real job: saying who you are in one place a machine will read.",
|
|
250
|
+
tr: "Schema zengin sonuç garantisi vermez; SSS zengin sonucu Mayıs 2026’da kalktı. Asıl işi: kim olduğunuzu makinenin okuyacağı tek bir yerde söylemek.",
|
|
251
|
+
},
|
|
252
|
+
katmanNote: {
|
|
253
|
+
en: "Treat schema as hygiene, not a lever: a 1,885-page difference-in-differences study found no citation lift. Do it right and quickly, then spend the time on situation pages and mentions.",
|
|
254
|
+
tr: "Schema’yı kaldıraç değil temizlik işi sayın: 1.885 sayfalık bir fark-içinde-fark çalışması alıntılanmada artış bulmadı. Doğru ve hızlı yapın, zamanı durum sayfalarına ve bahsedilmeye ayırın.",
|
|
255
|
+
},
|
|
256
|
+
verifiedBy: [
|
|
257
|
+
{ tool: "katman_audit_site", check: { en: "pages[].jsonLdErrors = 0 and the expected types in pages[].jsonLdTypes", tr: "pages[].jsonLdErrors = 0 ve pages[].jsonLdTypes’ta beklenen tipler" } },
|
|
258
|
+
{ tool: "katman_generate", check: { en: "organization-jsonld / website-jsonld / software-jsonld built from research (not-X in disambiguatingDescription)", tr: "organization-jsonld / website-jsonld / software-jsonld araştırmadan üretilir (not-X disambiguatingDescription’da)" } },
|
|
259
|
+
{ tool: "manual", check: { en: "Rich Results Test (search.google.com/test/rich-results) shows no errors", tr: "Zengin Sonuçlar Testi (search.google.com/test/rich-results) hata göstermiyor" } },
|
|
260
|
+
],
|
|
261
|
+
gates: ["quote"],
|
|
262
|
+
week: 2,
|
|
263
|
+
changesCode: true,
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
id: "P6",
|
|
267
|
+
tag: { en: "FOR EACH SITUATION", tr: "HER DURUM İÇİN" },
|
|
268
|
+
title: { en: "Write a situation page", tr: "Durum sayfası yaz" },
|
|
269
|
+
when: {
|
|
270
|
+
en: "Week 3, once for every customer situation in research (start with 3–5).",
|
|
271
|
+
tr: "3. hafta: araştırmadaki her müşteri durumu için bir kez (önce 3–5 tane).",
|
|
272
|
+
},
|
|
273
|
+
body: {
|
|
274
|
+
tr: `research.md'deki şu durum için yeni bir sayfa yaz: "{situation}"
|
|
275
|
+
URL: {situation_url}
|
|
276
|
+
- H1: müşterinin kendi cümlesi.
|
|
277
|
+
- İlk 1-3 cümle soruya doğrudan cevap: müşteri ne getirir, biz ne yaparız, eline ne geçer, hangi korkusu gereksiz (markanın tek cümlesi: "{sentence}").
|
|
278
|
+
- Hemen deneme alanı ya da tek bir net eylem (kayıt, dosya yükleme, fiyat).
|
|
279
|
+
- Kanıt: research.md'deki sayı, tarihiyle.
|
|
280
|
+
- Nasıl çalışır: 3-5 adım, düz metin.
|
|
281
|
+
- Kimler için değil: 2-3 madde.
|
|
282
|
+
- Dürüst karşılaştırma: alternatifler ve hangisi kime uygun.
|
|
283
|
+
- 5-8 soruluk SSS; sorular müşterinin ağzından.
|
|
284
|
+
- İlgili 3-5 sayfaya metinli link; sayfanın altında güncelleme tarihi ve yazar.
|
|
285
|
+
P3 ve P5'teki kurallara uy. Sayfayı sitemap'e ve ilgili sayfalardan linke ekle.`,
|
|
286
|
+
en: `Write a new page for this situation from research.md: "{situation}"
|
|
287
|
+
URL: {situation_url}
|
|
288
|
+
- H1: the customer's own sentence.
|
|
289
|
+
- The first 1–3 sentences answer the question directly: what the customer brings, what we do, what they get, which fear is unnecessary (the brand's one sentence: "{sentence}").
|
|
290
|
+
- An immediate try-it area or one clear action (sign-up, file upload, pricing).
|
|
291
|
+
- Proof: the number from research.md, with its date.
|
|
292
|
+
- How it works: 3–5 steps, plain text.
|
|
293
|
+
- Who it's not for: 2–3 bullets.
|
|
294
|
+
- An honest comparison: the alternatives and which one fits whom.
|
|
295
|
+
- An FAQ with 5–8 questions, in the customer's words.
|
|
296
|
+
- Text links to 3–5 related pages; the update date and the author at the bottom of the page.
|
|
297
|
+
Follow the rules in P3 and P5. Add the page to the sitemap and link to it from related pages.`,
|
|
298
|
+
},
|
|
299
|
+
accept: {
|
|
300
|
+
en: "Read on their own, the first three sentences answer the question; no [FILL] is left on the page; at least one page links to it.",
|
|
301
|
+
tr: "İlk üç cümle tek başına okununca soruyu cevaplıyor; sayfada [DOLDUR] kalmadı; en az bir sayfadan link alıyor.",
|
|
302
|
+
},
|
|
303
|
+
why: {
|
|
304
|
+
en: "On the sample site, 59% of ChatGPT visitors landed on two situation pages; 8 of the 17 paying customers came from one of them.",
|
|
305
|
+
tr: "Örnek sitede ChatGPT ziyaretçilerinin %59’u iki durum sayfasına indi; ödeme yapan 17 müşterinin 8’i birinden geldi.",
|
|
306
|
+
},
|
|
307
|
+
katmanNote: {
|
|
308
|
+
en: "Put the key claim in the H1 and the first ~200 characters: ChatGPT's index is reported to keep about 200 characters around the H1. Publish 5–10 such pages a week at most; mass-produced intent pages count as scaled content abuse.",
|
|
309
|
+
tr: "Asıl iddiayı H1’e ve ilk ~200 karaktere koyun: ChatGPT’nin dizininin H1 çevresinde yaklaşık 200 karakter tuttuğu bildiriliyor. Haftada en fazla 5–10 böyle sayfa yayınlayın; seri üretilmiş niyet sayfaları ölçekli içerik istismarı (scaled content abuse) sayılır.",
|
|
310
|
+
},
|
|
311
|
+
verifiedBy: [
|
|
312
|
+
{ tool: "katman_lint_content", check: { en: "page_type \"situation\": first sentences answer, no placeholders, FAQ 5–8, not-for, alternatives, ≥3 internal links", tr: "page_type \"situation\": ilk cümleler cevap veriyor, yer tutucu yok, SSS 5–8, kimler için değil, alternatifler, ≥3 iç link" } },
|
|
313
|
+
{ tool: "katman_audit_site", check: { en: "urls mode after deploy: 200, wordsJsOff, firstParagraph, internalLinksIn ≥ 1, inSitemap", tr: "yayından sonra urls modu: 200, wordsJsOff, firstParagraph, internalLinksIn ≥ 1, inSitemap" } },
|
|
314
|
+
],
|
|
315
|
+
gates: ["fit", "quote"],
|
|
316
|
+
week: 3,
|
|
317
|
+
changesCode: true,
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
id: "P7",
|
|
321
|
+
title: { en: "A comparison guide that names competitors", tr: "Rakipleri de sayan karşılaştırma rehberi" },
|
|
322
|
+
when: {
|
|
323
|
+
en: "Week 3, once research lists at least 3 competitors with a source and a date.",
|
|
324
|
+
tr: "3. hafta: araştırmada kaynağı ve tarihi olan en az 3 rakip varken.",
|
|
325
|
+
},
|
|
326
|
+
body: {
|
|
327
|
+
tr: `"{category} için hangi araçlar var?" sorusuna tarafsız bir rehber yaz.
|
|
328
|
+
- research.md'deki rakipleri say ({competitors}). Her birinin ne yaptığını ve fiyatını kendi sitesinden al; kaynak linkini ve kontrol tarihini yanına yaz.
|
|
329
|
+
- Bizi listenin başına koyma. Her araç için "kime uygun" ve "neyi yapmıyor" yaz; bizim için de.
|
|
330
|
+
- En üste 2-3 cümlelik kısa cevap koy: hangi durumda hangi araç.
|
|
331
|
+
- Article + ItemList schema; güncelleme tarihi görünür olsun.`,
|
|
332
|
+
en: `Write a neutral guide answering "Which {category} tools are there?"
|
|
333
|
+
- Cover the competitors in research.md ({competitors}). Take what each one does and its price from its own website; put the source link and the date you checked next to it.
|
|
334
|
+
- Don't put us at the top of the list. For every tool write "who it fits" and "what it doesn't do" — for us too.
|
|
335
|
+
- At the top, put a 2–3 sentence short answer: which tool for which situation.
|
|
336
|
+
- Article + ItemList schema; the update date must be visible.`,
|
|
337
|
+
},
|
|
338
|
+
accept: {
|
|
339
|
+
en: "Every competitor fact has a source and a date next to it; our own gaps are written down too.",
|
|
340
|
+
tr: "Her rakip bilgisinin yanında kaynak ve tarih var; bizim eksiğimiz de yazılı.",
|
|
341
|
+
},
|
|
342
|
+
why: {
|
|
343
|
+
en: "The sample site's two list guides got 24 visitors from ChatGPT in their first week. Per our research notes, assistants often skip the author's brand in lists where the author ranks itself first (Ahrefs).",
|
|
344
|
+
tr: "Örnek sitenin iki liste rehberi ilk haftada ChatGPT’den 24 ziyaretçi aldı. Araştırma notumuza göre kendini birinci yazan listelerde yazarın markası sık sık atlanıyor (Ahrefs).",
|
|
345
|
+
},
|
|
346
|
+
katmanNote: {
|
|
347
|
+
en: "Self-ranked \"best X\" lists now backfire: when a brand's own list was cited, the brand was left out of the recommendation 69% of the time, and such lists carry spam-policy risk since 2026. Be on the list, don't be the referee.",
|
|
348
|
+
tr: "Kendini birinci yazan “en iyi X” listeleri artık ters tepiyor: markanın kendi listesi alıntılandığında markanın %69 oranında öneriden çıkarıldığı görüldü; 2026’dan beri bu listeler spam politikası riski de taşıyor. Listede olun, hakem olmayın.",
|
|
349
|
+
},
|
|
350
|
+
verifiedBy: [
|
|
351
|
+
{ tool: "katman_lint_content", check: { en: "page_type \"comparison\": a source and a date near every competitor, our own gaps written, brand not ranked first, update date", tr: "page_type \"comparison\": her rakibin yanında kaynak ve tarih, kendi eksiğimiz yazılı, marka birinci sırada değil, güncelleme tarihi" } },
|
|
352
|
+
{ tool: "katman_visibility_run", check: { en: "later: \"best\" and \"alternative\" questions of the question set", tr: "sonra: soru setindeki “en iyi” ve “alternatif” soruları" } },
|
|
353
|
+
],
|
|
354
|
+
gates: ["fit", "mention"],
|
|
355
|
+
week: 3,
|
|
356
|
+
changesCode: true,
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
id: "P8",
|
|
360
|
+
title: { en: "llms.txt and the \"not-X\" note", tr: "llms.txt ve “X değildir” notu" },
|
|
361
|
+
when: {
|
|
362
|
+
en: "Week 3, once the one sentence, the pricing and the not-X note are settled in research.",
|
|
363
|
+
tr: "3. hafta: tek cümle, fiyat ve “X değildir” notu araştırmada netleşince.",
|
|
364
|
+
},
|
|
365
|
+
body: {
|
|
366
|
+
tr: `Kökte /llms.txt oluştur (düz metin, Markdown):
|
|
367
|
+
# {brand}
|
|
368
|
+
> {sentence}
|
|
369
|
+
Sonra: markanın ne olmadığı ("{not_x}"), fiyatlar, kimin için olduğu, en önemli 10-20 sayfanın linki ve her birine bir cümle.
|
|
370
|
+
llms.txt'yi sayfalarla aynı veri dosyasından üret; llms.txt'deki her fiyat ve iddia sitede birebir aynı olsun.`,
|
|
371
|
+
en: `Create /llms.txt at the root (plain text, Markdown):
|
|
372
|
+
# {brand}
|
|
373
|
+
> {sentence}
|
|
374
|
+
Then: what the brand is not ("{not_x}"), the prices, who it's for, links to the 10–20 most important pages with one sentence each.
|
|
375
|
+
Generate llms.txt from the same data file as the pages; every price and claim in llms.txt must match the site exactly.`,
|
|
376
|
+
},
|
|
377
|
+
accept: {
|
|
378
|
+
en: "Not a single price or claim conflicts between llms.txt and the pages.",
|
|
379
|
+
tr: "llms.txt ile sayfalar arasında çelişen tek bir fiyat ya da iddia yok.",
|
|
380
|
+
},
|
|
381
|
+
why: {
|
|
382
|
+
en: "On the sample site, llms.txt once stated a different price than a feature page; we fixed it. Per our research notes Google doesn't use llms.txt; the file is for assistants.",
|
|
383
|
+
tr: "Örnek sitede llms.txt bir ara özellik sayfasından farklı fiyat yazıyordu; düzelttik. Araştırma notumuza göre Google llms.txt kullanmıyor; bu dosya asistanlar içindir.",
|
|
384
|
+
},
|
|
385
|
+
katmanNote: {
|
|
386
|
+
en: "Cheap to ship, never a lever: a 300k-domain study found no effect on citations. Do it after rendering (P2) is fixed, keep it short, and keep it identical to the pages.",
|
|
387
|
+
tr: "Yapması ucuz, ama kaldıraç değil: 300 bin alan adılık bir çalışma alıntılanmaya etkisini bulamadı. Render (P2) düzeldikten sonra yapın, kısa tutun, sayfalarla birebir aynı tutun.",
|
|
388
|
+
},
|
|
389
|
+
verifiedBy: [
|
|
390
|
+
{ tool: "katman_generate", check: { en: "artifact llms.txt rebuilt from research (same sentence, pricing, not-X, live situation pages)", tr: "llms.txt çıktısı araştırmadan yeniden üretilir (aynı cümle, fiyat, “X değildir”, yayındaki durum sayfaları)" } },
|
|
391
|
+
{ tool: "katman_lint_content", check: { en: "the one-sentence check compares the page, research and llms.txt", tr: "tek cümle kontrolü sayfayı, araştırmayı ve llms.txt’yi karşılaştırır" } },
|
|
392
|
+
{ tool: "katman_audit_site", check: { en: "site.llmsTxt.exists and the research-consistency findings", tr: "site.llmsTxt.exists ve araştırma tutarlılığı bulguları" } },
|
|
393
|
+
],
|
|
394
|
+
gates: ["quote"],
|
|
395
|
+
week: 3,
|
|
396
|
+
changesCode: true,
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
id: "P9",
|
|
400
|
+
title: { en: "Images: alt text, size, compression", tr: "Görseller: alt metin, boyut, sıkıştırma" },
|
|
401
|
+
when: {
|
|
402
|
+
en: "Week 2, when images lack alt, width or height, or weigh more than 200 KB.",
|
|
403
|
+
tr: "2. hafta: görsellerde alt, width ya da height eksikse veya 200 KB’ı geçiyorsa.",
|
|
404
|
+
},
|
|
405
|
+
body: {
|
|
406
|
+
tr: `Tüm <img> etiketlerini tara:
|
|
407
|
+
- Bilgi taşıyan görsele içeriğini anlatan kısa alt metin yaz; süs görseline alt="".
|
|
408
|
+
- Her görsele width ve height ver (sayfa kaymasın).
|
|
409
|
+
- İlk ekrandaki büyük görsel hariç loading="lazy".
|
|
410
|
+
- Görselleri gösterildikleri boyutun en fazla 2 katına küçült; WebP, AVIF ya da sıkıştırılmış PNG kaydet.
|
|
411
|
+
Önce/sonra dosya boyutlarını tablo olarak ver.`,
|
|
412
|
+
en: `Go through every <img> tag:
|
|
413
|
+
- Give images that carry information a short alt text describing what they show; decorative images get alt="".
|
|
414
|
+
- Give every image a width and a height (so the page doesn't shift).
|
|
415
|
+
- loading="lazy" on everything except the large image on the first screen.
|
|
416
|
+
- Shrink images to at most 2x the size they're displayed at; save them as WebP, AVIF or compressed PNG.
|
|
417
|
+
Give the before/after file sizes as a table.`,
|
|
418
|
+
},
|
|
419
|
+
accept: {
|
|
420
|
+
en: "No <img> without alt text; no image over 200 KB; CLS below 0.1.",
|
|
421
|
+
tr: "Alt metni olmayan <img> yok; 200 KB’ı geçen görsel yok; CLS 0,1’in altında.",
|
|
422
|
+
},
|
|
423
|
+
why: {
|
|
424
|
+
en: "Images are usually the heaviest part of a page; this is where speed and LCP improve.",
|
|
425
|
+
tr: "Görseller genelde sayfanın en ağır parçası; hız ve LCP buradan düzelir.",
|
|
426
|
+
},
|
|
427
|
+
verifiedBy: [
|
|
428
|
+
{ tool: "katman_scan_code", check: { en: "images without alt, width or height in the source", tr: "kaynakta alt, width ya da height’ı olmayan görseller" } },
|
|
429
|
+
{ tool: "katman_audit_site", check: { en: "pages[].imagesMissingAlt = 0", tr: "pages[].imagesMissingAlt = 0" } },
|
|
430
|
+
{ tool: "manual", check: { en: "Lighthouse (mobile) for CLS", tr: "CLS için Lighthouse (mobil)" } },
|
|
431
|
+
],
|
|
432
|
+
gates: ["read"],
|
|
433
|
+
week: 2,
|
|
434
|
+
changesCode: true,
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
id: "P10",
|
|
438
|
+
title: { en: "Internal links and broken links", tr: "İç linkler ve kırık linkler" },
|
|
439
|
+
when: {
|
|
440
|
+
en: "Week 2, when pages get no internal links, links are broken, or menus navigate with onClick.",
|
|
441
|
+
tr: "2. hafta: sayfalar iç link almıyorsa, linkler kırıksa ya da menüler onClick ile yönlendiriyorsa.",
|
|
442
|
+
},
|
|
443
|
+
body: {
|
|
444
|
+
tr: `Tüm herkese açık sayfaların iç link haritasını çıkar.
|
|
445
|
+
1. Hiçbir sayfadan link almayan (yetim) sayfaları listele; her birine ana sayfadan, altbilgiden ya da ilgili bir sayfadan metinli link ver.
|
|
446
|
+
2. Link metni "tıklayın" ya da "devamı" değil, hedef sayfanın konusu olsun.
|
|
447
|
+
3. Tüm iç linkleri kontrol et: 404 ya da yönlendirme zinciri varsa linki doğrudan son adrese çevir.
|
|
448
|
+
4. Menüde ve kartlarda onClick yönlendirmesi varsa <a href> ile değiştir.
|
|
449
|
+
Önce/sonra: her sayfanın aldığı iç link sayısı.`,
|
|
450
|
+
en: `Map the internal links of all public pages.
|
|
451
|
+
1. List the pages that get no link from any page (orphans); give each one a text link from the home page, the footer or a related page.
|
|
452
|
+
2. Link text should be the target page's topic, not "click here" or "read more".
|
|
453
|
+
3. Check every internal link: if one hits a 404 or a redirect chain, point it straight at the final URL.
|
|
454
|
+
4. If menus or cards navigate with onClick, replace them with <a href>.
|
|
455
|
+
Before/after: the number of internal links each page receives.`,
|
|
456
|
+
},
|
|
457
|
+
accept: { en: "0 orphan pages, 0 broken internal links.", tr: "Yetim sayfa 0, kırık iç link 0." },
|
|
458
|
+
why: {
|
|
459
|
+
en: "On the sample site this was the reason 35 pages weren't indexed: no page referred to them. Linking them from the footer solved it.",
|
|
460
|
+
tr: "Örnek sitede dizine girmeyen 35 sayfanın sebebi buydu: yönlendiren sayfa yok. Altbilgiye link verince çözüldü.",
|
|
461
|
+
},
|
|
462
|
+
verifiedBy: [
|
|
463
|
+
{ tool: "katman_audit_site", check: { en: "pages[].internalLinksIn ≥ 1 for every page (target ≥ 3), 0 broken internal links", tr: "her sayfada pages[].internalLinksIn ≥ 1 (hedef ≥ 3), kırık iç link 0" } },
|
|
464
|
+
{ tool: "katman_scan_code", check: { en: "no onClick navigation or hash routing on public pages", tr: "herkese açık sayfalarda onClick yönlendirmesi ya da # yönlendirmesi yok" } },
|
|
465
|
+
],
|
|
466
|
+
gates: ["read"],
|
|
467
|
+
week: 2,
|
|
468
|
+
changesCode: true,
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
id: "P11",
|
|
472
|
+
title: { en: "404s, redirects, preview URLs", tr: "404, yönlendirme, önizleme adresleri" },
|
|
473
|
+
when: {
|
|
474
|
+
en: "Week 2, when unknown URLs return 200, redirects are missing, or a preview copy of the site is public.",
|
|
475
|
+
tr: "2. hafta: olmayan adresler 200 dönüyorsa, yönlendirmeler eksikse ya da sitenin önizleme kopyası açıksa.",
|
|
476
|
+
},
|
|
477
|
+
body: {
|
|
478
|
+
tr: `1. Bulunmayan her adres gerçek 404 durum kodu dönsün. Tek sayfa uygulamada "sayfa bulunamadı" ekranı 200 dönüyorsa sunucuda 404 veren bir adrese yönlendir ya da o ekrana noindex ekle.
|
|
479
|
+
2. Adı değişen sayfalar eski adresten 301 ile yenisine gitsin.
|
|
480
|
+
3. http → https ve www ↔ yalın alan adı tek bir adrese kalıcı olarak yönlensin.
|
|
481
|
+
4. Önizleme ve staging adresleri (ör. proje.vercel.app) ya asıl alan adına 301 ile gitsin ya da noindex dönsün ya da şifreli olsun. robots.txt bunun için yetmez.`,
|
|
482
|
+
en: `1. Every URL that doesn't exist must return a real 404 status code. If a single-page app's "page not found" screen returns 200, redirect to a URL that returns 404 on the server, or add noindex to that screen.
|
|
483
|
+
2. Renamed pages redirect from the old URL to the new one with a 301.
|
|
484
|
+
3. http → https and www ↔ bare domain redirect permanently to one single address.
|
|
485
|
+
4. Preview and staging addresses (e.g. project.vercel.app) either 301 to the main domain, return noindex, or are password-protected. robots.txt isn't enough for this.`,
|
|
486
|
+
},
|
|
487
|
+
accept: {
|
|
488
|
+
en: "curl on /this-page-does-not-exist → 404; http://, www. and the preview address → the main address, or noindex.",
|
|
489
|
+
tr: "curl ile /olmayan-sayfa → 404; http://, www. ve önizleme adresi → asıl adres ya da noindex.",
|
|
490
|
+
},
|
|
491
|
+
why: {
|
|
492
|
+
en: "Google counts a 200 that says \"page not found\" as a soft 404, and sees an open demo copy as another version of the same site.",
|
|
493
|
+
tr: "Google “sayfa yok” diyen 200’ü sahte 404 sayar; açık kalmış bir demo kopyasını da aynı sitenin başka bir hâli olarak görür.",
|
|
494
|
+
},
|
|
495
|
+
katmanNote: {
|
|
496
|
+
en: "404s take about 35% of AI-crawler fetches in large log studies: fix broken URLs and soft 404s before adding pages.",
|
|
497
|
+
tr: "Büyük kayıt çalışmalarında yapay zekâ tarayıcılarının isteklerinin yaklaşık %35’i 404’e gidiyor: sayfa eklemeden önce kırık adresleri ve sahte 404’leri düzeltin.",
|
|
498
|
+
},
|
|
499
|
+
verifiedBy: [
|
|
500
|
+
{ tool: "katman_audit_site", check: { en: "site.soft404 false, site.httpsRedirect and site.hostRedirect true, preview-copy checks pass", tr: "site.soft404 false, site.httpsRedirect ve site.hostRedirect true, önizleme kopyası kontrolleri geçiyor" } },
|
|
501
|
+
{ tool: "katman_scan_code", check: { en: "hosting.previewPatterns lists the preview hosts to redirect or noindex", tr: "hosting.previewPatterns yönlendirilecek ya da noindex yapılacak önizleme adreslerini listeler" } },
|
|
502
|
+
],
|
|
503
|
+
gates: ["read"],
|
|
504
|
+
week: 2,
|
|
505
|
+
changesCode: true,
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
id: "P12",
|
|
509
|
+
title: { en: "Speed and mobile", tr: "Hız ve mobil" },
|
|
510
|
+
when: {
|
|
511
|
+
en: "Week 2, on the home page and the 3 most important pages.",
|
|
512
|
+
tr: "2. hafta: ana sayfada ve en önemli 3 sayfada.",
|
|
513
|
+
},
|
|
514
|
+
body: {
|
|
515
|
+
tr: `Ana sayfa ve en önemli 3 sayfa için mobil Lighthouse çalıştır:
|
|
516
|
+
npx lighthouse {site_url}/SAYFA --only-categories=performance,seo,accessibility --form-factor=mobile --view
|
|
517
|
+
Hedef: LCP en fazla 2,5 sn, CLS en fazla 0,1, uzun görevler yok (INP için hedef 200 ms).
|
|
518
|
+
Bulduğun sorunları düzelt: kullanılmayan JavaScript'i ayır, yazı tiplerini önden yükle, görsellere boyut ver, üçüncü taraf betikleri ertele.
|
|
519
|
+
Her sayfada <meta name="viewport" content="width=device-width, initial-scale=1"> olsun; 360 px genişlikte yatay kaydırma olmasın.`,
|
|
520
|
+
en: `Run mobile Lighthouse on the home page and the 3 most important pages:
|
|
521
|
+
npx lighthouse {site_url}/PAGE --only-categories=performance,seo,accessibility --form-factor=mobile --view
|
|
522
|
+
Target: LCP at most 2.5 s, CLS at most 0.1, no long tasks (INP target 200 ms).
|
|
523
|
+
Fix what you find: split unused JavaScript, preload fonts, give images dimensions, defer third-party scripts.
|
|
524
|
+
Every page needs <meta name="viewport" content="width=device-width, initial-scale=1">; no horizontal scrolling at 360 px width.`,
|
|
525
|
+
},
|
|
526
|
+
accept: {
|
|
527
|
+
en: "On all three pages LCP ≤ 2.5 s and CLS ≤ 0.1; no horizontal scrolling at 360 px.",
|
|
528
|
+
tr: "Üç sayfada da LCP ≤ 2,5 sn ve CLS ≤ 0,1; 360 px’te yatay kaydırma yok.",
|
|
529
|
+
},
|
|
530
|
+
why: {
|
|
531
|
+
en: "Google indexes the mobile version of the site. 71% of the sample site's ChatGPT visitors were on mobile.",
|
|
532
|
+
tr: "Google sitenin mobil hâlini dizine alır. Örnek siteye ChatGPT’den gelenlerin %71’i mobildeydi.",
|
|
533
|
+
},
|
|
534
|
+
verifiedBy: [
|
|
535
|
+
{ tool: "manual", check: { en: "the Lighthouse command above (mobile), on 4 pages", tr: "yukarıdaki Lighthouse komutu (mobil), 4 sayfada" } },
|
|
536
|
+
{ tool: "katman_audit_site", check: { en: "site.compression (br/gzip), page bytes and response times", tr: "site.compression (br/gzip), sayfa boyutu ve yanıt süreleri" } },
|
|
537
|
+
],
|
|
538
|
+
gates: ["read"],
|
|
539
|
+
week: 2,
|
|
540
|
+
changesCode: true,
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
id: "P13",
|
|
544
|
+
title: { en: "Measurement: ChatGPT visitors and bots", tr: "Ölçüm: ChatGPT ziyaretçisi ve botlar" },
|
|
545
|
+
when: {
|
|
546
|
+
en: "Week 4, so that assistant visitors and bots are counted correctly.",
|
|
547
|
+
tr: "4. hafta: asistandan gelen ziyaretçi ve botlar doğru sayılsın diye.",
|
|
548
|
+
},
|
|
549
|
+
body: {
|
|
550
|
+
tr: `1. Analitikte ChatGPT'den gelenleri ayrı bir kanal olarak say: referrer chatgpt.com olanlar VE linkinde utm_source=chatgpt.com olanlar.
|
|
551
|
+
2. Sunucu tarafında, user-agent'a göre bot isteklerini günlük say: GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-User, PerplexityBot, Googlebot, Bingbot. (Botlar tarayıcıdaki analitik kodunu çalıştırmaz.)
|
|
552
|
+
3. İnsan filtresi ekle: sayfayı kaydıran, 2'den fazla sayfa gören, kaydolan ya da ödeme yapan oturum insan sayılsın; gerisi ayrı tutulsun.
|
|
553
|
+
4. Yönetim panelinde son 7 günü kanal kanal göster.`,
|
|
554
|
+
en: `1. In analytics, count visitors from ChatGPT as a separate channel: those with referrer chatgpt.com AND those with utm_source=chatgpt.com in the link.
|
|
555
|
+
2. On the server, count bot requests per day by user-agent: GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-User, PerplexityBot, Googlebot, Bingbot. (Bots don't run the analytics script in the browser.)
|
|
556
|
+
3. Add a human filter: a session counts as human if it scrolled, viewed more than 2 pages, signed up or paid; keep the rest separate.
|
|
557
|
+
4. Show the last 7 days channel by channel in the admin panel.`,
|
|
558
|
+
},
|
|
559
|
+
accept: {
|
|
560
|
+
en: "The ChatGPT channel also counts visits without a referrer; bot counts come from the server.",
|
|
561
|
+
tr: "ChatGPT kanalı referrer’sız ziyaretleri de sayıyor; bot sayıları sunucudan geliyor.",
|
|
562
|
+
},
|
|
563
|
+
why: {
|
|
564
|
+
en: "79% of the sample site's ChatGPT visitors sent no referrer; a single PDF crawler created 704 fake sessions.",
|
|
565
|
+
tr: "Örnek sitede ChatGPT ziyaretçilerinin %79’u referrer göndermedi; tek bir PDF tarayıcısı 704 sahte oturum yarattı.",
|
|
566
|
+
},
|
|
567
|
+
katmanNote: {
|
|
568
|
+
en: "Also turn on the official citation reports: the Search Console Gen-AI report (worldwide since 31 Aug 2026) and Bing Webmaster Tools AI Performance (since Feb 2026, shows Copilot citations).",
|
|
569
|
+
tr: "Resmî alıntı raporlarını da açın: Search Console Gen-AI raporu (31 Ağustos 2026’dan beri dünya genelinde) ve Bing Webmaster Tools AI Performance (Şubat 2026’dan beri; Copilot alıntılarını gösterir).",
|
|
570
|
+
},
|
|
571
|
+
verifiedBy: [
|
|
572
|
+
{ tool: "katman_scan_code", check: { en: "analytics.tracksAiChannel is true", tr: "analytics.tracksAiChannel true" } },
|
|
573
|
+
{ tool: "katman_generate", check: { en: "analytics-channel-snippet (human filter + channel rule) and bot-counter-snippet for your platform", tr: "analytics-channel-snippet (insan filtresi + kanal kuralı) ve platformunuza göre bot-counter-snippet" } },
|
|
574
|
+
],
|
|
575
|
+
gates: ["measure"],
|
|
576
|
+
week: 4,
|
|
577
|
+
changesCode: true,
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
id: "P14",
|
|
581
|
+
tag: { en: "EVERY RELEASE", tr: "HER YAYINDA" },
|
|
582
|
+
title: { en: "Final check before publishing", tr: "Yayından önce son kontrol" },
|
|
583
|
+
when: {
|
|
584
|
+
en: "Before every release and after every technical fix.",
|
|
585
|
+
tr: "Her yayından önce ve her teknik düzeltmeden sonra.",
|
|
586
|
+
},
|
|
587
|
+
body: {
|
|
588
|
+
tr: `Yayından önce herkese açık her sayfa için şu kontrolleri çalıştır ve sonucu tablo olarak ver:
|
|
589
|
+
durum kodu 200 | istenmeyen noindex yok | title ve description benzersiz | canonical kendine | og:image açılıyor | JSON-LD geçerli JSON | JavaScript kapalıyken H1 ve ilk paragraf görünüyor | sitemap'te var | en az 1 iç link alıyor.
|
|
590
|
+
Ayrıca: /olmayan-sayfa 404 dönüyor mu; GPTBot, OAI-SearchBot ve ClaudeBot user-agent'ıyla ana sayfa 200 dönüyor mu (403 ise CDN ya da güvenlik duvarı engelliyor).
|
|
591
|
+
HTML yanıtında Content-Encoding br ya da gzip var mı; sayfaya en az 3 iç link geliyor mu (hedef).
|
|
592
|
+
Bu kontrolü bir betik olarak kaydet (ör. npm run seo-check) ki her yayında tekrar çalışsın. Her teknik düzeltmeden sonra da çalıştır: örnek sitede bir düzeltme HTML sıkıştırmasını sessizce kapatmıştı.`,
|
|
593
|
+
en: `Before publishing, run these checks on every public page and give the result as a table:
|
|
594
|
+
status code 200 | no unwanted noindex | unique title and description | canonical points to itself | og:image opens | JSON-LD is valid JSON | H1 and first paragraph visible with JavaScript off | in the sitemap | receives at least 1 internal link.
|
|
595
|
+
Also: does /this-page-does-not-exist return 404; does the home page return 200 to the GPTBot, OAI-SearchBot and ClaudeBot user-agents (403 means the CDN or firewall is blocking)?
|
|
596
|
+
Does the HTML response carry Content-Encoding br or gzip; does each page receive at least 3 internal links (target)?
|
|
597
|
+
Save this check as a script (e.g. npm run seo-check) so it runs on every release. Also run it after every technical fix: on the sample site, one fix silently turned off HTML compression.`,
|
|
598
|
+
},
|
|
599
|
+
accept: {
|
|
600
|
+
en: "Every cell in the table passes; the script is in the repo.",
|
|
601
|
+
tr: "Tablonun bütün hücreleri geçti; betik repoda.",
|
|
602
|
+
},
|
|
603
|
+
why: {
|
|
604
|
+
en: "Without this check we wouldn't have seen the missing share card and the bot block on katman.pro.",
|
|
605
|
+
tr: "Bugün katman.pro’da bu kontrol olmasaydı paylaşım kartı eksikliğini ve bot engelini görmeyecektik.",
|
|
606
|
+
},
|
|
607
|
+
katmanNote: {
|
|
608
|
+
en: "katman_audit_site with urls: [...] is exactly this table. As the release script: `\"seo-check\": \"npx -y katman-mcp audit <your site URL> --strict\"` — it exits with code 2 on critical findings, so CI fails before a broken release ships.",
|
|
609
|
+
tr: "katman_audit_site’ı urls: [...] ile çağırmak tam olarak bu tabloyu verir. Yayın betiği olarak: `\"seo-check\": \"npx -y katman-mcp audit <site adresiniz> --strict\"` — kritik bulguda 2 koduyla çıkar, böylece bozuk bir yayın gitmeden CI durur.",
|
|
610
|
+
},
|
|
611
|
+
verifiedBy: [
|
|
612
|
+
{ tool: "katman_audit_site", check: { en: "urls mode (quick verify): every column passes for every page, AI bots 200, real 404, compression on", tr: "urls modu (hızlı doğrulama): her sayfada her sütun geçiyor, yapay zekâ botları 200, gerçek 404, sıkıştırma açık" } },
|
|
613
|
+
],
|
|
614
|
+
gates: ["read", "quote"],
|
|
615
|
+
week: 4,
|
|
616
|
+
changesCode: false,
|
|
617
|
+
},
|
|
618
|
+
];
|
|
619
|
+
export function getPrompt(id) {
|
|
620
|
+
const m = /^p?\s*(\d{1,2})$/i.exec(id.trim());
|
|
621
|
+
const norm = m ? `P${Number(m[1])}` : id.trim().toUpperCase();
|
|
622
|
+
return PROMPTS.find((p) => p.id === norm);
|
|
623
|
+
}
|
|
624
|
+
export function promptPlaceholders(p) {
|
|
625
|
+
const found = new Set();
|
|
626
|
+
for (const text of [p.body.en, p.body.tr]) {
|
|
627
|
+
for (const m of text.matchAll(/\{([a-z_]+)\}/g)) {
|
|
628
|
+
if (PLACEHOLDERS.includes(m[1]))
|
|
629
|
+
found.add(m[1]);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return [...found];
|
|
633
|
+
}
|