@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.
- package/assets/rules/architecture.md +15 -0
- package/assets/rules/bottom-sheet.md +20 -0
- package/assets/rules/compose-color.md +22 -0
- package/assets/rules/compose-constraintlayout.md +18 -0
- package/assets/rules/compose-screen.md +33 -0
- package/assets/rules/compose-spacing.md +39 -0
- package/assets/rules/compose.md +54 -0
- package/assets/rules/dialog.md +33 -0
- package/assets/rules/domain.md +36 -0
- package/assets/rules/filesystem.md +3 -0
- package/assets/rules/git.md +11 -0
- package/assets/rules/github-readme.md +97 -0
- package/assets/rules/github-repo.md +65 -0
- package/assets/rules/kmp.md +14 -0
- package/assets/rules/kotlin.md +48 -0
- package/assets/rules/lazylist.md +36 -0
- package/assets/rules/mvi-error-handling.md +19 -0
- package/assets/rules/mvi-state.md +31 -0
- package/assets/rules/mvi.md +39 -0
- package/assets/rules/navigation.md +20 -0
- package/assets/rules/network.md +36 -0
- package/assets/rules/preview.md +31 -0
- package/assets/rules/realtime.md +39 -0
- package/assets/rules/resource.md +18 -0
- package/assets/rules/room.md +33 -0
- package/assets/rules/scaffold.md +25 -0
- package/assets/rules/shimmer.md +34 -0
- package/assets/rules/textfield.md +31 -0
- package/assets/rules/typography.md +32 -0
- package/assets/rules/usecase.md +60 -0
- package/assets/rules/workflow.md +17 -0
- package/assets/rules/workmanager.md +44 -0
- package/assets/skills/create-data-layer/SKILL.md +60 -0
- package/assets/skills/create-datastore-preference/SKILL.md +80 -0
- package/assets/skills/create-domain-mapper/SKILL.md +30 -0
- package/assets/skills/create-feature-alert-dialog/SKILL.md +549 -0
- package/assets/skills/create-feature-bottom-sheet/SKILL.md +300 -0
- package/assets/skills/create-feature-scaffold-screen/SKILL.md +74 -0
- package/assets/skills/create-feature-scaffold-screen/references/form-screen.md +20 -0
- package/assets/skills/create-feature-scaffold-screen/references/network-only-screen.md +20 -0
- package/assets/skills/create-feature-scaffold-screen/references/paginated-screen.md +29 -0
- package/assets/skills/create-feature-scaffold-screen/references/room-backed-screen.md +23 -0
- package/assets/skills/create-feature-scaffold-screen/references/static-screen.md +13 -0
- package/assets/skills/create-guide/SKILL.md +169 -0
- package/assets/skills/create-guide/references/android-project.md +120 -0
- package/assets/skills/create-guide/references/androidx.md +49 -0
- package/assets/skills/create-guide/references/existing-guides.yaml +60 -0
- package/assets/skills/create-guide/references/github.md +71 -0
- package/assets/skills/create-guide/references/guide-defaults.yaml +51 -0
- package/assets/skills/create-guide/references/notion.md +121 -0
- package/assets/skills/create-guide/references/output-contract.md +79 -0
- package/assets/skills/create-guide/references/research.md +64 -0
- package/assets/skills/create-guide/references/validation.md +59 -0
- package/assets/skills/create-guide/scripts/validate_guide.py +167 -0
- package/assets/skills/create-guide/templates/guide-manifest.yaml +67 -0
- package/assets/skills/create-guide/templates/implementation-plan.md +30 -0
- package/assets/skills/create-guide/templates/notion-page.md +46 -0
- package/assets/skills/create-guide/templates/repository/AGENTS.md +7 -0
- package/assets/skills/create-guide/templates/repository/README.md +32 -0
- package/assets/skills/create-guide/templates/repository/SOURCES.md +32 -0
- package/assets/skills/create-guide/templates/validation-report.md +30 -0
- package/assets/skills/create-guide/tests/test_validate_guide.py +97 -0
- package/assets/skills/create-ktor-endpoint/SKILL.md +33 -0
- package/assets/skills/create-notification-flow/SKILL.md +103 -0
- package/assets/skills/create-offline-outbox/SKILL.md +110 -0
- package/assets/skills/create-paging-flow/SKILL.md +96 -0
- package/assets/skills/create-project-from-template/SKILL.md +91 -0
- package/assets/skills/create-project-from-template/assets/icons/android.svg +12 -0
- package/assets/skills/create-project-from-template/assets/icons/compose.svg +34 -0
- package/assets/skills/create-project-from-template/assets/icons/jetpack.svg +10 -0
- package/assets/skills/create-project-from-template/references/myapplication-checklist.md +92 -0
- package/assets/skills/create-room-storage/SKILL.md +39 -0
- package/assets/skills/create-shared-component/SKILL.md +160 -0
- package/assets/skills/create-signalr-channel/SKILL.md +39 -0
- package/assets/skills/create-usecase/SKILL.md +142 -0
- package/assets/skills/create-workmanager-task/SKILL.md +41 -0
- package/assets/skills/github-repo-settings/SKILL.md +152 -0
- package/assets/skills/interview-me/SKILL.md +150 -0
- package/dist/errors.js +49 -0
- package/dist/frontmatter.js +44 -0
- package/dist/github.js +23 -0
- package/dist/index.js +6 -0
- package/dist/server.js +131 -0
- package/dist/source/bundled.js +78 -0
- package/dist/source/cache.js +61 -0
- package/dist/source/github-source.js +68 -0
- package/dist/source/github.js +185 -0
- package/dist/source/index.js +36 -0
- package/dist/source/types.js +1 -0
- package/dist/validation.js +33 -0
- package/dist/version.js +26 -0
- 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
|
+
[](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
|
+
[](https://github.com/michaelbel/<Repository>)
|
|
15
|
+
[](https://github.com/michaelbel/<Repository>)
|
|
16
|
+
[](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 или аналогичный шаблон.
|