| 1 | # -*- coding: utf-8 -*- |
| 2 | from pathlib import Path |
| 3 | |
| 4 | CSS = "/assets/css/site.css" |
| 5 | ORG = "https://github.com/AI-Guiders" |
| 6 | DOCS_CIDE = "https://ai-guiders.github.io/cascade-ide/" |
| 7 | |
| 8 | CARDS_EN = [ |
| 9 | ("🚀", "cascade-ide", f'C#-first IDE, in-proc MCP, cockpit attention model. <a href="{DOCS_CIDE}">Docs</a>. MIT.', ["IDE", "Agents"]), |
| 10 | ("📝", "agent-notes-mcp", "Memory MCP 2.0, one TOML config. With kb-public.", ["MCP", "Memory"]), |
| 11 | ("📖", "kb-public", "Public KB slice for agents (CC BY-SA).", ["Knowledge", "CC BY-SA"]), |
| 12 | ("🔍", "RoslynMcp", "Roslyn diagnostics, code actions, rename.", ["C#", "Roslyn"]), |
| 13 | ("🔧", "dotnet-debug-mcp", "DAP debugging for agents.", ["MCP", "Debug"]), |
| 14 | ("📚", "agent-first-learn", "Course: designing with and for AI agents. MIT.", ["Course", "Agents"]), |
| 15 | ] |
| 16 | |
| 17 | CARDS_RU = [ |
| 18 | ("🚀", "cascade-ide", f'IDE на C#, in-proc MCP, модель внимания кокпита. <a href="{DOCS_CIDE}">Документация</a>. MIT.', ["IDE", "Агенты"]), |
| 19 | ("📝", "agent-notes-mcp", "MCP памяти 2.0, один TOML. В паре с kb-public.", ["MCP", "Память"]), |
| 20 | ("📖", "kb-public", "Публичный срез KB для агентов (CC BY-SA).", ["Knowledge", "CC BY-SA"]), |
| 21 | ("🔍", "RoslynMcp", "Диагностики Roslyn, code actions, rename.", ["C#", "Roslyn"]), |
| 22 | ("🔧", "dotnet-debug-mcp", "Отладка DAP для агентов.", ["MCP", "Debug"]), |
| 23 | ("📚", "agent-first-learn", "Курс: проектирование с агентами и для агентов. MIT.", ["Курс", "Агенты"]), |
| 24 | ] |
| 25 | |
| 26 | |
| 27 | def cards_html(cards): |
| 28 | parts = [] |
| 29 | for icon, slug, desc, tags in cards: |
| 30 | d = desc.replace("kb-public", f'<a href="{ORG}/kb-public">kb-public</a>') |
| 31 | tag_html = "".join(f'<span class="tag">{t}</span>' for t in tags) |
| 32 | parts.append( |
| 33 | " <div class=\"project-card\">\n" |
| 34 | " <div class=\"card-header\">\n" |
| 35 | f" <motion class=\"card-icon\" aria-hidden=\"true\">{icon}</motion>\n" |
| 36 | f" <h3><a href=\"{ORG}/{slug}\">{slug}</a></h3>\n" |
| 37 | " </motion>\n" |
| 38 | f" <p class=\"desc\">{d}</p>\n" |
| 39 | f" <div class=\"tags\">{tag_html}</div>\n" |
| 40 | " </div>" |
| 41 | ) |
| 42 | text = "\n".join(parts) |
| 43 | return text.replace("motion", "div") |
| 44 | |
| 45 | |
| 46 | def page(lang, title, desc, nav_items, hero_label, h1, h1_accent, lead, about_title, about_text, |
| 47 | stack_title, stack_sub, handbook_title, handbook_sub, handbook_items, footer_links, css_path, base): |
| 48 | nav_lis = "\n".join(f' <li><a href="{href}">{label}</a></li>' for href, label in nav_items) |
| 49 | lang_en = 'aria-current="page"' if lang == "en" else "" |
| 50 | lang_ru = 'aria-current="page"' if lang == "ru" else "" |
| 51 | cards = cards_html(CARDS_EN if lang == "en" else CARDS_RU) |
| 52 | hb = "\n".join( |
| 53 | f' <li><a href="{u}">{t}</a><span class="meta">{m}</span></li>' |
| 54 | for u, t, m in handbook_items |
| 55 | ) |
| 56 | fl = "\n".join(f' <a href="{u}">{t}</a>' for u, t in footer_links) |
| 57 | skip = "К содержанию" if lang == "ru" else "Skip to content" |
| 58 | nav_aria = "Основная навигация" if lang == "ru" else "Main navigation" |
| 59 | lang_aria = "Язык" if lang == "ru" else "Language" |
| 60 | btn_org = "GitHub org" |
| 61 | btn_stack = "Open stack" |
| 62 | |
| 63 | html = f"""<!DOCTYPE html> |
| 64 | <html lang="{lang}"> |
| 65 | <head> |
| 66 | <meta charset="utf-8" /> |
| 67 | <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| 68 | <title>{title}</title> |
| 69 | <meta name="description" content="{desc}" /> |
| 70 | <link rel="alternate" hreflang="en" href="/" /> |
| 71 | <link rel="alternate" hreflang="ru" href="/ru/" /> |
| 72 | <link rel="alternate" hreflang="x-default" href="/" /> |
| 73 | <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🤖</text></svg>" /> |
| 74 | <link rel="stylesheet" href="{css_path}" /> |
| 75 | </head> |
| 76 | <body> |
| 77 | <a href="#main" class="skip-link">{skip}</a> |
| 78 | <nav aria-label="{nav_aria}"> |
| 79 | <div class="nav-inner"> |
| 80 | <div class="logo"><a href="{base}">AI<span>-</span>Guiders</a></div> |
| 81 | <div class="nav-right"> |
| 82 | <ul> |
| 83 | {nav_lis} |
| 84 | </ul> |
| 85 | <ul class="nav-lang" aria-label="{lang_aria}"> |
| 86 | <li><a href="/" lang="en" {lang_en}>EN</a></li> |
| 87 | <li class="nav-sep" aria-hidden="true">|</li> |
| 88 | <li><a href="/ru/" lang="ru" {lang_ru}>RU</a></li> |
| 89 | </ul> |
| 90 | </motion> |
| 91 | </motion> |
| 92 | </nav> |
| 93 | <main id="main"> |
| 94 | <section class="hero"> |
| 95 | <div class="container"> |
| 96 | <div class="hero-content"> |
| 97 | <div class="hero-label">{hero_label}</div> |
| 98 | <h1>{h1}<br /><span class="accent">{h1_accent}</span></h1> |
| 99 | <p class="lead">{lead}</p> |
| 100 | <div class="hero-buttons"> |
| 101 | <a href="{ORG}" class="btn btn-primary">{btn_org}</a> |
| 102 | <a href="#stack" class="btn btn-outline">{btn_stack}</a> |
| 103 | </motion> |
| 104 | </motion> |
| 105 | </motion> |
| 106 | </section> |
| 107 | <section id="about"> |
| 108 | <div class="container"> |
| 109 | <h2 class="section-title">{about_title}</h2> |
| 110 | <p class="section-subtitle">{about_text}</p> |
| 111 | </motion> |
| 112 | </section> |
| 113 | <section id="stack"> |
| 114 | <div class="container"> |
| 115 | <h2 class="section-title">{stack_title}</h2> |
| 116 | <p class="section-subtitle">{stack_sub}</p> |
| 117 | <div class="card-grid"> |
| 118 | {cards} |
| 119 | </motion> |
| 120 | </motion> |
| 121 | </section> |
| 122 | <section id="handbook"> |
| 123 | <div class="container"> |
| 124 | <h2 class="section-title">{handbook_title}</h2> |
| 125 | <p class="section-subtitle">{handbook_sub}</p> |
| 126 | <ul class="link-list"> |
| 127 | {hb} |
| 128 | </ul> |
| 129 | </motion> |
| 130 | </section> |
| 131 | </main> |
| 132 | <footer> |
| 133 | <div class="links"> |
| 134 | {fl} |
| 135 | </motion> |
| 136 | <p>AI-Guiders © 2026</p> |
| 137 | </footer> |
| 138 | </body> |
| 139 | </html> |
| 140 | """ |
| 141 | return html.replace("motion", "div") |
| 142 | |
| 143 | |
| 144 | root = Path(__file__).resolve().parent.parent / "docs" |
| 145 | (root / "ru").mkdir(parents=True, exist_ok=True) |
| 146 | |
| 147 | en = page( |
| 148 | "en", |
| 149 | "AI-Guiders — Agent-first open stack for .NET", |
| 150 | "Open tools for agent-first .NET work: Cascade IDE, MCP servers, shared libraries.", |
| 151 | [("#about", "About"), ("#stack", "Open stack"), (DOCS_CIDE, "CIDE docs"), ("/writing/", "Writing"), ("#handbook", "Handbook"), (ORG, "GitHub")], |
| 152 | "Open source · .NET · MCP", |
| 153 | "Agent-first tools", |
| 154 | "you can verify", |
| 155 | "We build an IDE, MCP servers, and shared libraries so humans and coding agents " |
| 156 | "share the same ground truth — build, tests, git, Roslyn, and agent memory.", |
| 157 | "What we do", |
| 158 | "<strong>Parity</strong> — if an agent can do it via MCP, the IDE should not contradict it. " |
| 159 | "<strong>Observable loop</strong> — one legible contour for editor, tools, and agent. " |
| 160 | "Org essays in <a href=\"/writing/\">Writing</a>; longer personal archive on " |
| 161 | "<a href=\"https://karataevdmitry.github.io/writing/\">Dmitry Karataev's site</a>.", |
| 162 | "Open stack", |
| 163 | f"Full list: <a href=\"{ORG}\">github.com/AI-Guiders</a> · <a href=\"{DOCS_CIDE}\">Cascade IDE documentation</a> (ADR by status, RU/EN)", |
| 164 | "Handbook & KB", |
| 165 | "Org norms and agent knowledge.", |
| 166 | [ |
| 167 | (f"{ORG}/handbook/wiki", "Handbook wiki", "Mission, values, boundaries (Russian)"), |
| 168 | (f"{ORG}/kb-public", "kb-public", "Public KB for agents"), |
| 169 | (f"{ORG}/handbook", "handbook repo", "Edit wiki/ folder, sync to GitHub Wiki"), |
| 170 | ], |
| 171 | [(ORG, "GitHub"), ("/writing/", "Writing"), ("https://karataevdmitry.github.io/writing/", "Personal archive")], |
| 172 | CSS, |
| 173 | "/", |
| 174 | ) |
| 175 | ru = page( |
| 176 | "ru", |
| 177 | "AI-Guiders — open stack для agent-first .NET", |
| 178 | "Открытые инструменты для agent-first .NET: Cascade IDE, MCP, общие библиотеки.", |
| 179 | [("#about", "О нас"), ("#stack", "Open stack"), (DOCS_CIDE, "Доки CIDE"), ("/ru/writing/", "Тексты"), ("#handbook", "Handbook"), (ORG, "GitHub")], |
| 180 | "Open source · .NET · MCP", |
| 181 | "Инструменты для агентов", |
| 182 | "которым можно доверять", |
| 183 | "IDE, MCP-серверы и общие библиотеки — чтобы человек и coding agent смотрели " |
| 184 | "на одну правду: сборка, тесты, git, Roslyn, память агента.", |
| 185 | "Что делаем", |
| 186 | "<strong>Паритет</strong> — если агент делает через MCP, IDE не должна противоречить. " |
| 187 | "<strong>Наблюдаемый контур</strong> — редактор, инструменты и агент в одной картине. " |
| 188 | "Орг-тексты — в разделе <a href=\"/ru/writing/\">Тексты</a>; личный архив — на " |
| 189 | "<a href=\"https://karataevdmitry.github.io/ru/writing/\">сайте Дмитрия Каратаева</a>.", |
| 190 | "Open stack", |
| 191 | f"Полный список: <a href=\"{ORG}\">github.com/AI-Guiders</a> · <a href=\"{DOCS_CIDE}\">документация Cascade IDE</a> (ADR по статусам, RU/EN)", |
| 192 | "Handbook и KB", |
| 193 | "Нормы организации и знания для агентов.", |
| 194 | [ |
| 195 | (f"{ORG}/handbook/wiki", "Handbook wiki", "Миссия, ценности, границы (RU)"), |
| 196 | (f"{ORG}/kb-public", "kb-public", "Публичный срез KB"), |
| 197 | (f"{ORG}/handbook", "репозиторий handbook", "Правки в wiki/, синхрон с GitHub Wiki"), |
| 198 | ], |
| 199 | [(ORG, "GitHub"), ("/ru/writing/", "Тексты"), ("https://karataevdmitry.github.io/ru/writing/", "Личный архив")], |
| 200 | "/assets/css/site.css", |
| 201 | "/ru/", |
| 202 | ) |
| 203 | |
| 204 | (root / "index.html").write_text(en, encoding="utf-8", newline="\n") |
| 205 | (root / "ru" / "index.html").write_text(ru, encoding="utf-8", newline="\n") |
| 206 | print("ok") |
| 207 | |