@michaelbel/cuckcoder-mcp 1.6.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/assets/rules/architecture.md +15 -0
  2. package/assets/rules/bottom-sheet.md +20 -0
  3. package/assets/rules/compose-color.md +22 -0
  4. package/assets/rules/compose-constraintlayout.md +18 -0
  5. package/assets/rules/compose-screen.md +33 -0
  6. package/assets/rules/compose-spacing.md +39 -0
  7. package/assets/rules/compose.md +54 -0
  8. package/assets/rules/dialog.md +33 -0
  9. package/assets/rules/domain.md +36 -0
  10. package/assets/rules/filesystem.md +3 -0
  11. package/assets/rules/git.md +11 -0
  12. package/assets/rules/github-readme.md +97 -0
  13. package/assets/rules/github-repo.md +65 -0
  14. package/assets/rules/kmp.md +14 -0
  15. package/assets/rules/kotlin.md +48 -0
  16. package/assets/rules/lazylist.md +36 -0
  17. package/assets/rules/mvi-error-handling.md +19 -0
  18. package/assets/rules/mvi-state.md +31 -0
  19. package/assets/rules/mvi.md +39 -0
  20. package/assets/rules/navigation.md +20 -0
  21. package/assets/rules/network.md +36 -0
  22. package/assets/rules/preview.md +31 -0
  23. package/assets/rules/realtime.md +39 -0
  24. package/assets/rules/resource.md +18 -0
  25. package/assets/rules/room.md +33 -0
  26. package/assets/rules/scaffold.md +25 -0
  27. package/assets/rules/shimmer.md +34 -0
  28. package/assets/rules/textfield.md +31 -0
  29. package/assets/rules/typography.md +32 -0
  30. package/assets/rules/usecase.md +60 -0
  31. package/assets/rules/workflow.md +17 -0
  32. package/assets/rules/workmanager.md +44 -0
  33. package/assets/skills/create-data-layer/SKILL.md +60 -0
  34. package/assets/skills/create-datastore-preference/SKILL.md +80 -0
  35. package/assets/skills/create-domain-mapper/SKILL.md +30 -0
  36. package/assets/skills/create-feature-alert-dialog/SKILL.md +549 -0
  37. package/assets/skills/create-feature-bottom-sheet/SKILL.md +300 -0
  38. package/assets/skills/create-feature-scaffold-screen/SKILL.md +74 -0
  39. package/assets/skills/create-feature-scaffold-screen/references/form-screen.md +20 -0
  40. package/assets/skills/create-feature-scaffold-screen/references/network-only-screen.md +20 -0
  41. package/assets/skills/create-feature-scaffold-screen/references/paginated-screen.md +29 -0
  42. package/assets/skills/create-feature-scaffold-screen/references/room-backed-screen.md +23 -0
  43. package/assets/skills/create-feature-scaffold-screen/references/static-screen.md +13 -0
  44. package/assets/skills/create-guide/SKILL.md +169 -0
  45. package/assets/skills/create-guide/references/android-project.md +120 -0
  46. package/assets/skills/create-guide/references/androidx.md +49 -0
  47. package/assets/skills/create-guide/references/existing-guides.yaml +60 -0
  48. package/assets/skills/create-guide/references/github.md +71 -0
  49. package/assets/skills/create-guide/references/guide-defaults.yaml +51 -0
  50. package/assets/skills/create-guide/references/notion.md +121 -0
  51. package/assets/skills/create-guide/references/output-contract.md +79 -0
  52. package/assets/skills/create-guide/references/research.md +64 -0
  53. package/assets/skills/create-guide/references/validation.md +59 -0
  54. package/assets/skills/create-guide/scripts/validate_guide.py +167 -0
  55. package/assets/skills/create-guide/templates/guide-manifest.yaml +67 -0
  56. package/assets/skills/create-guide/templates/implementation-plan.md +30 -0
  57. package/assets/skills/create-guide/templates/notion-page.md +46 -0
  58. package/assets/skills/create-guide/templates/repository/AGENTS.md +7 -0
  59. package/assets/skills/create-guide/templates/repository/README.md +32 -0
  60. package/assets/skills/create-guide/templates/repository/SOURCES.md +32 -0
  61. package/assets/skills/create-guide/templates/validation-report.md +30 -0
  62. package/assets/skills/create-guide/tests/test_validate_guide.py +97 -0
  63. package/assets/skills/create-ktor-endpoint/SKILL.md +33 -0
  64. package/assets/skills/create-notification-flow/SKILL.md +103 -0
  65. package/assets/skills/create-offline-outbox/SKILL.md +110 -0
  66. package/assets/skills/create-paging-flow/SKILL.md +96 -0
  67. package/assets/skills/create-project-from-template/SKILL.md +91 -0
  68. package/assets/skills/create-project-from-template/assets/icons/android.svg +12 -0
  69. package/assets/skills/create-project-from-template/assets/icons/compose.svg +34 -0
  70. package/assets/skills/create-project-from-template/assets/icons/jetpack.svg +10 -0
  71. package/assets/skills/create-project-from-template/references/myapplication-checklist.md +92 -0
  72. package/assets/skills/create-room-storage/SKILL.md +39 -0
  73. package/assets/skills/create-shared-component/SKILL.md +160 -0
  74. package/assets/skills/create-signalr-channel/SKILL.md +39 -0
  75. package/assets/skills/create-usecase/SKILL.md +142 -0
  76. package/assets/skills/create-workmanager-task/SKILL.md +41 -0
  77. package/assets/skills/github-repo-settings/SKILL.md +152 -0
  78. package/assets/skills/interview-me/SKILL.md +150 -0
  79. package/dist/errors.js +49 -0
  80. package/dist/frontmatter.js +44 -0
  81. package/dist/github.js +23 -0
  82. package/dist/index.js +6 -0
  83. package/dist/server.js +131 -0
  84. package/dist/source/bundled.js +78 -0
  85. package/dist/source/cache.js +61 -0
  86. package/dist/source/github-source.js +68 -0
  87. package/dist/source/github.js +185 -0
  88. package/dist/source/index.js +36 -0
  89. package/dist/source/types.js +1 -0
  90. package/dist/validation.js +33 -0
  91. package/dist/version.js +26 -0
  92. package/package.json +38 -0
@@ -0,0 +1,59 @@
1
+ # Validation Rules
2
+
3
+ ## Статическая проверка
4
+
5
+ До Gradle-проверок запусти:
6
+
7
+ ```shell
8
+ python scripts/validate_guide.py /path/to/project
9
+ ```
10
+
11
+ Исправь ошибки структуры, инструкций, imports, package, источников и ссылок.
12
+
13
+ ## Gradle
14
+
15
+ Обязательные задачи:
16
+
17
+ ```shell
18
+ ./gradlew :app:assembleDebug
19
+ ./gradlew :app:lintDebug
20
+ ```
21
+
22
+ Если есть unit-тесты:
23
+
24
+ ```shell
25
+ ./gradlew :app:testDebugUnitTest
26
+ ```
27
+
28
+ Если тема требует instrumented tests и среда предоставляет emulator/device, запусти их. Отсутствие emulator не блокирует публикацию только тогда, когда instrumented tests не заявлены обязательным сценарием.
29
+
30
+ ## Ручная проверка
31
+
32
+ Запусти приложение и проверь каждый сценарий, когда среда позволяет. Для визуальных компонентов проверь light/dark theme, системный Back и состояния, заявленные в манифесте.
33
+
34
+ ## Cross-check
35
+
36
+ После подготовки Notion сравни:
37
+
38
+ - title и topic;
39
+ - версии;
40
+ - имена классов и функций;
41
+ - package;
42
+ - соответствие примеров кода реализованным samples без упоминания их имен в Notion;
43
+ - ссылки на файлы;
44
+ - экспериментальные opt-in;
45
+ - ограничения;
46
+ - источник адаптированного кода.
47
+
48
+ ## Отчёт
49
+
50
+ Заполняй `.guidekit/validation-report.md` фактическими данными:
51
+
52
+ - команда;
53
+ - результат;
54
+ - время;
55
+ - commit SHA;
56
+ - причина пропуска;
57
+ - ручные проверки.
58
+
59
+ Не ставь `passed`, если команда не запускалась или завершилась неуспешно.
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import argparse
4
+ from pathlib import Path
5
+ import re
6
+ import subprocess
7
+ import sys
8
+
9
+
10
+ REQUIRED_PATHS = (
11
+ ".github/CODEOWNERS",
12
+ ".github/FUNDING.yml",
13
+ ".github/workflows/ci.yml",
14
+ ".guidekit/manifest.yaml",
15
+ ".guidekit/research.md",
16
+ ".guidekit/implementation-plan.md",
17
+ ".guidekit/SOURCES.md",
18
+ ".guidekit/validation-report.md",
19
+ ".idea/icon.svg",
20
+ "AGENTS.md",
21
+ "CLAUDE.md",
22
+ "GEMINI.md",
23
+ "README.md",
24
+ "app/build.gradle.kts",
25
+ "build.gradle.kts",
26
+ "gradle/libs.versions.toml",
27
+ "gradlew",
28
+ "settings.gradle.kts",
29
+ )
30
+
31
+ WILDCARD_IMPORT = re.compile(r"^import\s+[\w.]+\.\*$", re.MULTILINE)
32
+ PACKAGE_DECLARATION = re.compile(r"^package\s+([\w.]+)", re.MULTILINE)
33
+ SAMPLE_DIRECTORY = re.compile(r"sample\d{2}_[A-Za-z0-9_]+$")
34
+
35
+
36
+ def instruction_link_error(path: Path) -> str | None:
37
+ if path.is_symlink():
38
+ if path.readlink() != Path("AGENTS.md"):
39
+ return f"{path.name} must point to AGENTS.md"
40
+ return None
41
+
42
+ if path.is_file() and path.read_text(encoding="utf-8").strip() == "AGENTS.md":
43
+ return None
44
+
45
+ return f"{path.name} must be a symlink to AGENTS.md"
46
+
47
+
48
+ def run_command(project: Path, command: list[str]) -> tuple[bool, str]:
49
+ result = subprocess.run(
50
+ command,
51
+ cwd=project,
52
+ check=False,
53
+ text=True,
54
+ )
55
+ return result.returncode == 0, " ".join(command)
56
+
57
+
58
+ def validate(project: Path) -> list[str]:
59
+ errors: list[str] = []
60
+
61
+ for relative_path in REQUIRED_PATHS:
62
+ path = project / relative_path
63
+ if not path.exists() and not path.is_symlink():
64
+ errors.append(f"Missing required path: {relative_path}")
65
+
66
+ for name in ("CLAUDE.md", "GEMINI.md"):
67
+ error = instruction_link_error(project / name)
68
+ if error:
69
+ errors.append(error)
70
+
71
+ agents_path = project / "AGENTS.md"
72
+ if agents_path.exists():
73
+ agents = agents_path.read_text(encoding="utf-8")
74
+ for phrase in ("cuckcoder", ".guidekit/manifest.yaml", ".guidekit/SOURCES.md"):
75
+ if phrase not in agents:
76
+ errors.append(f"AGENTS.md does not mention: {phrase}")
77
+
78
+ manifest_path = project / ".guidekit/manifest.yaml"
79
+ if manifest_path.exists():
80
+ manifest = manifest_path.read_text(encoding="utf-8")
81
+ for section in ("guide:", "topic:", "repository:", "scenarios:", "notion:", "validation:"):
82
+ if section not in manifest:
83
+ errors.append(f"Manifest misses section: {section}")
84
+ if re.search(r"<[^>\n]+>", manifest):
85
+ errors.append("Manifest still contains template placeholders")
86
+
87
+ source_root = project / "app/src"
88
+ kotlin_files = tuple(source_root.rglob("*.kt")) if source_root.exists() else ()
89
+ if not kotlin_files:
90
+ errors.append("No Kotlin source files found under app/src")
91
+
92
+ for kotlin_file in kotlin_files:
93
+ content = kotlin_file.read_text(encoding="utf-8")
94
+ relative_path = kotlin_file.relative_to(project)
95
+ if WILDCARD_IMPORT.search(content):
96
+ errors.append(f"Wildcard import in {relative_path}")
97
+ package_match = PACKAGE_DECLARATION.search(content)
98
+ if package_match and not package_match.group(1).startswith("org.michaelbel."):
99
+ errors.append(f"Unexpected package in {relative_path}: {package_match.group(1)}")
100
+
101
+ sample_directories = {
102
+ path
103
+ for kotlin_file in kotlin_files
104
+ for path in kotlin_file.parents
105
+ if path != source_root and SAMPLE_DIRECTORY.fullmatch(path.name)
106
+ }
107
+ readme_path = project / "README.md"
108
+ if readme_path.exists():
109
+ readme = readme_path.read_text(encoding="utf-8")
110
+ if "shields.io/github/last-commit/" not in readme:
111
+ errors.append("README misses the last-commit badge")
112
+ if len(sample_directories) > 1 and "## Samples" not in readme:
113
+ errors.append("Catalog project README misses the Samples section")
114
+
115
+ sources_path = project / ".guidekit/SOURCES.md"
116
+ if sources_path.exists():
117
+ sources = sources_path.read_text(encoding="utf-8")
118
+ if "https://" not in sources:
119
+ errors.append("SOURCES.md does not contain any source URL")
120
+ if re.search(r"<[^>\n]+>", sources):
121
+ errors.append("SOURCES.md still contains template placeholders")
122
+
123
+ return errors
124
+
125
+
126
+ def main() -> int:
127
+ parser = argparse.ArgumentParser(description="Validate an Android guide repository")
128
+ parser.add_argument("project", type=Path)
129
+ parser.add_argument("--run-gradle", action="store_true")
130
+ args = parser.parse_args()
131
+
132
+ project = args.project.resolve()
133
+ if not project.is_dir():
134
+ print(f"ERROR: Project directory does not exist: {project}")
135
+ return 1
136
+
137
+ errors = validate(project)
138
+ if errors:
139
+ for error in errors:
140
+ print(f"ERROR: {error}")
141
+ return 1
142
+
143
+ if args.run_gradle:
144
+ gradlew = project / "gradlew"
145
+ commands = (
146
+ [str(gradlew), ":app:assembleDebug"],
147
+ [str(gradlew), ":app:lintDebug"],
148
+ )
149
+ for command in commands:
150
+ passed, rendered = run_command(project, command)
151
+ if not passed:
152
+ print(f"ERROR: Gradle command failed: {rendered}")
153
+ return 1
154
+
155
+ unit_test_sources = tuple((project / "app/src/test").rglob("*.kt"))
156
+ if unit_test_sources:
157
+ passed, rendered = run_command(project, [str(gradlew), ":app:testDebugUnitTest"])
158
+ if not passed:
159
+ print(f"ERROR: Gradle command failed: {rendered}")
160
+ return 1
161
+
162
+ print(f"Guide repository is valid: {project}")
163
+ return 0
164
+
165
+
166
+ if __name__ == "__main__":
167
+ sys.exit(main())
@@ -0,0 +1,67 @@
1
+ schema_version: 1
2
+
3
+ guide:
4
+ id: <topic-slug>
5
+ title: <Topic>
6
+ notion_title: Гайд по <Topic>. <Практический результат>
7
+ audience: middle_android_developer
8
+ language: ru
9
+ status: draft
10
+
11
+ topic:
12
+ kind: android_api
13
+ api_status: <stable|alpha|experimental|platform>
14
+ primary_components:
15
+ - <Component>
16
+ related_components: []
17
+ artifact_coordinates: []
18
+ platform_api_level: null
19
+
20
+ research:
21
+ researched_at: <YYYY-MM-DD>
22
+ primary_sources: []
23
+ androidx:
24
+ used: false
25
+ repository: https://github.com/androidx/androidx
26
+ ref: null
27
+ paths: []
28
+ usage: <reference|adapted|copied|null>
29
+
30
+ repository:
31
+ owner: michaelbel
32
+ name: <Topic>
33
+ visibility: public
34
+ package: org.michaelbel.<topic>
35
+ template: https://github.com/michaelbel/MyApplication
36
+ template_ref: <commit-sha>
37
+ project_mode: <catalog|scenario|single>
38
+ default_branch: <branch>
39
+
40
+ android:
41
+ min_sdk: <inherited-or-required-value>
42
+ compile_sdk: <inherited-value>
43
+ target_sdk: <inherited-value>
44
+ jdk: 21
45
+ ui: compose
46
+ architecture: minimal_for_topic
47
+
48
+ scenarios:
49
+ - id: <scenario-id>
50
+ title: <Scenario title>
51
+ demonstrates:
52
+ - <API or behavior>
53
+
54
+ notion:
55
+ data_source_name: POSTS
56
+ existing_page_policy: update_in_place
57
+ page_id: null
58
+ page_url: null
59
+
60
+ validation:
61
+ required:
62
+ - :app:assembleDebug
63
+ - :app:lintDebug
64
+ optional: []
65
+
66
+ decisions: []
67
+ known_limitations: []
@@ -0,0 +1,30 @@
1
+ # Implementation Plan
2
+
3
+ Тема: <Topic>
4
+ Тип проекта: <catalog | scenario | single>
5
+
6
+ ## Сценарии
7
+
8
+ ### 01. <Название>
9
+
10
+ - Демонстрирует:
11
+ - Файлы:
12
+ - UI-состояния:
13
+ - Проверка:
14
+
15
+ ## Общая инфраструктура
16
+
17
+ - Зависимости:
18
+ - Навигация:
19
+ - Тема:
20
+ - Тесты:
21
+
22
+ ## Notion mapping
23
+
24
+ - Раздел:
25
+ - Source file:
26
+ - GitHub URL:
27
+
28
+ ## Не входит в scope
29
+
30
+ - ...
@@ -0,0 +1,46 @@
1
+ <Короткое объяснение точного API и проблемы, которую он решает. Объясни специальные термины через наблюдаемое поведение. Если широкая тема поддерживает несколько вариантов, явно зафиксируй выбранный вариант.>
2
+
3
+ <Критичное требование, статус API или ограничение, если оно есть.>
4
+
5
+ <empty-block/>
6
+ ## Главная идея
7
+
8
+ <Ментальная модель и взаимодействие компонентов.>
9
+
10
+ <empty-block/>
11
+ ## Настройка манифеста
12
+
13
+ <Добавляй этот раздел только когда API требует permissions, services, activities или providers.>
14
+
15
+ ```xml
16
+ <uses-permission android:name="<permission>" />
17
+ ```
18
+
19
+ <empty-block/>
20
+ ## <Основной компонент>
21
+
22
+ ### Что делает
23
+
24
+ ### Как работает
25
+
26
+ ### Важные параметры и состояния
27
+
28
+ ### Минимальный пример
29
+
30
+ ```kotlin
31
+ // Код существует в GitHub-репозитории.
32
+ ```
33
+
34
+ ### Ошибки и важные условия
35
+
36
+ <empty-block/>
37
+ ## <Следующий компонент или метод API>
38
+
39
+ <Описание API и пример кода из проекта. Не делай эту секцию toggle.>
40
+
41
+ <empty-block/>
42
+ ## Документация
43
+
44
+ [<Официальный заголовок страницы> | Android Developers](<documentation-url>)
45
+ [<Официальный заголовок release notes> | Android Developers](<release-notes-url>)
46
+ Репозиторий – [<repository-url>](<repository-url>)
@@ -0,0 +1,7 @@
1
+ # Repository Guidelines
2
+
3
+ - Follow the `cuckcoder` MCP instructions for all code generation and review tasks.
4
+ - Read `.guidekit/manifest.yaml` before changing the scope, package, versions or scenarios.
5
+ - Keep README, `.guidekit/SOURCES.md`, `.guidekit/validation-report.md` and the linked Notion guide consistent with the implementation.
6
+ - Do not add dependencies or architectural layers that are unrelated to the guide topic.
7
+ - Re-run the required validation tasks before publishing changes.
@@ -0,0 +1,32 @@
1
+ [![last-commit](https://img.shields.io/github/last-commit/michaelbel/<Repository>?style=for-the-badge&logo=github&labelColor=3F464F)](https://github.com/michaelbel/<Repository>/commits)
2
+
3
+ <Topic>
4
+ =
5
+
6
+ <One or two sentences describing the Android samples and the API they demonstrate.>
7
+
8
+ <div align="left">
9
+ <img src=".github/app.jpg" alt="Application">
10
+ </div>
11
+
12
+ ## Technologies
13
+
14
+ [![Android](https://img.shields.io/badge/Android-50AE55?style=for-the-badge&logo=android&logoColor=F6F6F6)](https://github.com/michaelbel/<Repository>)
15
+ [![Kotlin](https://img.shields.io/badge/Kotlin-7F52FF?style=for-the-badge&logo=kotlin&logoColor=white)](https://github.com/michaelbel/<Repository>)
16
+ [![Compose](https://img.shields.io/badge/Compose-4285F4?style=for-the-badge&logo=jetpackcompose&logoColor=white)](https://github.com/michaelbel/<Repository>)
17
+
18
+ ## Samples
19
+
20
+ | # | Sample | Description |
21
+ |---|--------|-------------|
22
+ | 01 | [`<API>`](app/src/main/kotlin/org/michaelbel/<package>/sample01_<Name>/Sample01App.kt) | <What this sample demonstrates> |
23
+
24
+ ## Build
25
+
26
+ ```shell
27
+ ./gradlew :app:assembleDebug
28
+ ```
29
+
30
+ ## Sources
31
+
32
+ See [`.guidekit/SOURCES.md`](.guidekit/SOURCES.md).
@@ -0,0 +1,32 @@
1
+ # Sources
2
+
3
+ Исследовано: <YYYY-MM-DD>
4
+
5
+ ## Official documentation
6
+
7
+ - URL:
8
+ - Использовано:
9
+
10
+ ## Release notes
11
+
12
+ - URL:
13
+ - Версия:
14
+
15
+ ## AndroidX
16
+
17
+ - Repository: https://github.com/androidx/androidx
18
+ - Ref or commit SHA:
19
+ - Path:
20
+ - URL:
21
+ - Использовано:
22
+ - Usage: <reference | adapted | copied>
23
+ - Изменения:
24
+
25
+ ## Additional sources
26
+
27
+ - URL:
28
+ - Почему источник нужен:
29
+
30
+ ## Third-party notices
31
+
32
+ <Укажи лицензию и notices для adapted/copied материалов или напиши `None`.>
@@ -0,0 +1,30 @@
1
+ # Validation Report
2
+
3
+ Commit: <sha>
4
+ Дата: <YYYY-MM-DD>
5
+
6
+ ## Static checks
7
+
8
+ - `python scripts/validate_guide.py .` — <passed|failed|not-run>
9
+
10
+ ## Gradle
11
+
12
+ - `./gradlew :app:assembleDebug` — <passed|failed|not-run>
13
+ - `./gradlew :app:lintDebug` — <passed|failed|not-run>
14
+ - `./gradlew :app:testDebugUnitTest` — <passed|failed|not-applicable|not-run>
15
+
16
+ ## Manual scenarios
17
+
18
+ - <Scenario> — <passed|failed|not-run>
19
+
20
+ ## Notion cross-check
21
+
22
+ - Versions — <passed|failed|not-run>
23
+ - Class and function names — <passed|failed|not-run>
24
+ - GitHub paths — <passed|failed|not-run>
25
+ - Code snippets — <passed|failed|not-run>
26
+ - Known limitations — <passed|failed|not-run>
27
+
28
+ ## Notes
29
+
30
+ - ...
@@ -0,0 +1,97 @@
1
+ from pathlib import Path
2
+ import tempfile
3
+ import unittest
4
+
5
+ from scripts.validate_guide import REQUIRED_PATHS, validate
6
+
7
+
8
+ class ValidateGuideTest(unittest.TestCase):
9
+
10
+ def create_project(self, root: Path) -> Path:
11
+ for relative_path in REQUIRED_PATHS:
12
+ path = root / relative_path
13
+ path.parent.mkdir(parents=True, exist_ok=True)
14
+ if relative_path in ("CLAUDE.md", "GEMINI.md"):
15
+ path.symlink_to("AGENTS.md")
16
+ else:
17
+ path.write_text("\n", encoding="utf-8")
18
+
19
+ (root / "AGENTS.md").write_text(
20
+ "Use cuckcoder. Read .guidekit/manifest.yaml and .guidekit/SOURCES.md.\n",
21
+ encoding="utf-8",
22
+ )
23
+ (root / ".guidekit/manifest.yaml").write_text(
24
+ "\n".join(
25
+ (
26
+ "guide:",
27
+ " id: navigation3",
28
+ "topic:",
29
+ " kind: android_api",
30
+ "repository:",
31
+ " name: Navigation3",
32
+ "scenarios:",
33
+ " - id: basic",
34
+ "notion:",
35
+ " data_source_name: POSTS",
36
+ " existing_page_policy: update_in_place",
37
+ "validation:",
38
+ " required:",
39
+ " - :app:assembleDebug",
40
+ )
41
+ ),
42
+ encoding="utf-8",
43
+ )
44
+ (root / ".guidekit/SOURCES.md").write_text(
45
+ "https://developer.android.com/\n",
46
+ encoding="utf-8",
47
+ )
48
+ (root / "README.md").write_text(
49
+ "https://img.shields.io/github/last-commit/michaelbel/Navigation3\n",
50
+ encoding="utf-8",
51
+ )
52
+
53
+ kotlin_path = root / "app/src/main/kotlin/org/michaelbel/navigation3/MainActivity.kt"
54
+ kotlin_path.parent.mkdir(parents=True, exist_ok=True)
55
+ kotlin_path.write_text(
56
+ "\n".join(
57
+ (
58
+ "package org.michaelbel.navigation3",
59
+ "",
60
+ "import androidx.activity.ComponentActivity",
61
+ "",
62
+ "class MainActivity: ComponentActivity()",
63
+ )
64
+ ),
65
+ encoding="utf-8",
66
+ )
67
+ return kotlin_path
68
+
69
+ def test_valid_project_has_no_errors(self) -> None:
70
+ with tempfile.TemporaryDirectory() as directory:
71
+ project = Path(directory)
72
+ self.create_project(project)
73
+
74
+ self.assertEqual([], validate(project))
75
+
76
+ def test_wildcard_import_is_rejected(self) -> None:
77
+ with tempfile.TemporaryDirectory() as directory:
78
+ project = Path(directory)
79
+ kotlin_path = self.create_project(project)
80
+ kotlin_path.write_text(
81
+ "\n".join(
82
+ (
83
+ "package org.michaelbel.navigation3",
84
+ "",
85
+ "import androidx.compose.runtime.*",
86
+ )
87
+ ),
88
+ encoding="utf-8",
89
+ )
90
+
91
+ errors = validate(project)
92
+
93
+ self.assertTrue(any("Wildcard import" in error for error in errors))
94
+
95
+
96
+ if __name__ == "__main__":
97
+ unittest.main()
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: create-ktor-endpoint
3
+ description: >-
4
+ Use when пользователь просит добавить Ktor API endpoint, request/response-модели или настроить
5
+ отдельный HttpClient для нового backend. Не используй для Room, mapper, use case, WebSocket или
6
+ полного потока данных от API до UI.
7
+ metadata:
8
+ author: michaelbel
9
+ ---
10
+
11
+ # Создание Ktor endpoint
12
+
13
+ Сначала найди подходящий `NetworkService`, существующий `HttpClient`/qualifier и соседний endpoint.
14
+ Повторяй локальные соглашения авторизации, base URL, headers, query/path/body и обработки
15
+ `HttpResponse`; новый client создавай только когда существующий не подходит по backend или policy.
16
+
17
+ ## Реализация
18
+
19
+ - Добавь метод в тематический `NetworkService` и используй точный HTTP method/path контракта.
20
+ - Создай только необходимые request/response-модели, каждую в отдельном файле.
21
+ - Имена моделей заканчивай на `Request` и `Response`.
22
+ - Аннотируй каждую API-модель `@Serializable`, а каждое сериализуемое поле — `@SerialName` с точным
23
+ именем из wire-контракта.
24
+ - Выражай path/query/header/body/multipart явно и сохраняй nullable/optional семантику API; не
25
+ подставляй domain defaults в transport-модель.
26
+ - Не добавляй mapper, Room-запись или `UseCase`, если они не входят в запрос пользователя.
27
+
28
+ Если нужен новый client, подключи его через существующий DI-подход и настрой только необходимые
29
+ base URL, JSON, authentication, timeout, logging/Chucker параметры. Не логируй токены и чувствительные
30
+ body.
31
+
32
+ Проверь компиляцию модуля и существующие mock/API contract tests; для нового endpoint добавь тест,
33
+ если в проекте уже используется MockEngine или аналогичный шаблон.