kodu 2.2.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/README.md +24 -3
  2. package/bin/kodu.js +40 -0
  3. package/package.json +12 -67
  4. package/scripts/install.js +68 -0
  5. package/scripts/postinstall.js +22 -0
  6. package/AGENTS.md +0 -214
  7. package/__tests__/core/fs/fs.service.test.ts +0 -72
  8. package/__tests__/core/registry/registry.service.test.ts +0 -82
  9. package/__tests__/shared/cleaner/cleaner.service.test.ts +0 -102
  10. package/__tests__/shared/git/git.service.test.ts +0 -84
  11. package/__tests__/shared/runbook/runbook.service.test.ts +0 -104
  12. package/__tests__/shared/tokenizer/tokenizer.service.test.ts +0 -45
  13. package/biome.json +0 -50
  14. package/dist/package.json +0 -96
  15. package/dist/src/app.module.d.ts +0 -2
  16. package/dist/src/app.module.js +0 -42
  17. package/dist/src/app.module.js.map +0 -1
  18. package/dist/src/commands/clean/clean.command.d.ts +0 -37
  19. package/dist/src/commands/clean/clean.command.js +0 -240
  20. package/dist/src/commands/clean/clean.command.js.map +0 -1
  21. package/dist/src/commands/clean/clean.module.d.ts +0 -2
  22. package/dist/src/commands/clean/clean.module.js +0 -26
  23. package/dist/src/commands/clean/clean.module.js.map +0 -1
  24. package/dist/src/commands/init/init.command.d.ts +0 -10
  25. package/dist/src/commands/init/init.command.js +0 -96
  26. package/dist/src/commands/init/init.command.js.map +0 -1
  27. package/dist/src/commands/init/init.module.d.ts +0 -2
  28. package/dist/src/commands/init/init.module.js +0 -22
  29. package/dist/src/commands/init/init.module.js.map +0 -1
  30. package/dist/src/commands/ops/ops-add.command.d.ts +0 -18
  31. package/dist/src/commands/ops/ops-add.command.js +0 -102
  32. package/dist/src/commands/ops/ops-add.command.js.map +0 -1
  33. package/dist/src/commands/ops/ops-init.command.d.ts +0 -22
  34. package/dist/src/commands/ops/ops-init.command.js +0 -130
  35. package/dist/src/commands/ops/ops-init.command.js.map +0 -1
  36. package/dist/src/commands/ops/ops-list.command.d.ts +0 -12
  37. package/dist/src/commands/ops/ops-list.command.js +0 -73
  38. package/dist/src/commands/ops/ops-list.command.js.map +0 -1
  39. package/dist/src/commands/ops/ops-path.command.d.ts +0 -9
  40. package/dist/src/commands/ops/ops-path.command.js +0 -52
  41. package/dist/src/commands/ops/ops-path.command.js.map +0 -1
  42. package/dist/src/commands/ops/ops-runbook.command.d.ts +0 -12
  43. package/dist/src/commands/ops/ops-runbook.command.js +0 -81
  44. package/dist/src/commands/ops/ops-runbook.command.js.map +0 -1
  45. package/dist/src/commands/ops/ops-status.command.d.ts +0 -11
  46. package/dist/src/commands/ops/ops-status.command.js +0 -62
  47. package/dist/src/commands/ops/ops-status.command.js.map +0 -1
  48. package/dist/src/commands/ops/ops-use.command.d.ts +0 -12
  49. package/dist/src/commands/ops/ops-use.command.js +0 -76
  50. package/dist/src/commands/ops/ops-use.command.js.map +0 -1
  51. package/dist/src/commands/ops/ops.command.d.ts +0 -7
  52. package/dist/src/commands/ops/ops.command.js +0 -56
  53. package/dist/src/commands/ops/ops.command.js.map +0 -1
  54. package/dist/src/commands/ops/ops.helpers.d.ts +0 -2
  55. package/dist/src/commands/ops/ops.helpers.js +0 -11
  56. package/dist/src/commands/ops/ops.helpers.js.map +0 -1
  57. package/dist/src/commands/ops/ops.module.d.ts +0 -2
  58. package/dist/src/commands/ops/ops.module.js +0 -36
  59. package/dist/src/commands/ops/ops.module.js.map +0 -1
  60. package/dist/src/commands/pack/pack.command.d.ts +0 -51
  61. package/dist/src/commands/pack/pack.command.js +0 -355
  62. package/dist/src/commands/pack/pack.command.js.map +0 -1
  63. package/dist/src/commands/pack/pack.module.d.ts +0 -2
  64. package/dist/src/commands/pack/pack.module.js +0 -27
  65. package/dist/src/commands/pack/pack.module.js.map +0 -1
  66. package/dist/src/core/config/config.module.d.ts +0 -2
  67. package/dist/src/core/config/config.module.js +0 -23
  68. package/dist/src/core/config/config.module.js.map +0 -1
  69. package/dist/src/core/config/config.schema.d.ts +0 -19
  70. package/dist/src/core/config/config.schema.js +0 -56
  71. package/dist/src/core/config/config.schema.js.map +0 -1
  72. package/dist/src/core/config/config.service.d.ts +0 -7
  73. package/dist/src/core/config/config.service.js +0 -49
  74. package/dist/src/core/config/config.service.js.map +0 -1
  75. package/dist/src/core/config/prompt.service.d.ts +0 -10
  76. package/dist/src/core/config/prompt.service.js +0 -80
  77. package/dist/src/core/config/prompt.service.js.map +0 -1
  78. package/dist/src/core/file-system/fs.module.d.ts +0 -2
  79. package/dist/src/core/file-system/fs.module.js +0 -21
  80. package/dist/src/core/file-system/fs.module.js.map +0 -1
  81. package/dist/src/core/file-system/fs.service.d.ts +0 -27
  82. package/dist/src/core/file-system/fs.service.js +0 -203
  83. package/dist/src/core/file-system/fs.service.js.map +0 -1
  84. package/dist/src/core/registry/registry.module.d.ts +0 -2
  85. package/dist/src/core/registry/registry.module.js +0 -22
  86. package/dist/src/core/registry/registry.module.js.map +0 -1
  87. package/dist/src/core/registry/registry.schema.d.ts +0 -24
  88. package/dist/src/core/registry/registry.schema.js +0 -21
  89. package/dist/src/core/registry/registry.schema.js.map +0 -1
  90. package/dist/src/core/registry/registry.service.d.ts +0 -16
  91. package/dist/src/core/registry/registry.service.js +0 -91
  92. package/dist/src/core/registry/registry.service.js.map +0 -1
  93. package/dist/src/core/ui/ui.module.d.ts +0 -2
  94. package/dist/src/core/ui/ui.module.js +0 -22
  95. package/dist/src/core/ui/ui.module.js.map +0 -1
  96. package/dist/src/core/ui/ui.service.d.ts +0 -22
  97. package/dist/src/core/ui/ui.service.js +0 -43
  98. package/dist/src/core/ui/ui.service.js.map +0 -1
  99. package/dist/src/main.d.ts +0 -2
  100. package/dist/src/main.js +0 -16
  101. package/dist/src/main.js.map +0 -1
  102. package/dist/src/shared/cleaner/cleaner.service.d.ts +0 -23
  103. package/dist/src/shared/cleaner/cleaner.service.js +0 -223
  104. package/dist/src/shared/cleaner/cleaner.service.js.map +0 -1
  105. package/dist/src/shared/cleaner/cleaner.types.d.ts +0 -21
  106. package/dist/src/shared/cleaner/cleaner.types.js +0 -3
  107. package/dist/src/shared/cleaner/cleaner.types.js.map +0 -1
  108. package/dist/src/shared/constants.d.ts +0 -4
  109. package/dist/src/shared/constants.js +0 -113
  110. package/dist/src/shared/constants.js.map +0 -1
  111. package/dist/src/shared/deps/deps.module.d.ts +0 -2
  112. package/dist/src/shared/deps/deps.module.js +0 -21
  113. package/dist/src/shared/deps/deps.module.js.map +0 -1
  114. package/dist/src/shared/deps/deps.service.d.ts +0 -15
  115. package/dist/src/shared/deps/deps.service.js +0 -114
  116. package/dist/src/shared/deps/deps.service.js.map +0 -1
  117. package/dist/src/shared/git/git.module.d.ts +0 -2
  118. package/dist/src/shared/git/git.module.js +0 -21
  119. package/dist/src/shared/git/git.module.js.map +0 -1
  120. package/dist/src/shared/git/git.service.d.ts +0 -5
  121. package/dist/src/shared/git/git.service.js +0 -56
  122. package/dist/src/shared/git/git.service.js.map +0 -1
  123. package/dist/src/shared/runbook/runbook.module.d.ts +0 -2
  124. package/dist/src/shared/runbook/runbook.module.js +0 -22
  125. package/dist/src/shared/runbook/runbook.module.js.map +0 -1
  126. package/dist/src/shared/runbook/runbook.service.d.ts +0 -20
  127. package/dist/src/shared/runbook/runbook.service.js +0 -118
  128. package/dist/src/shared/runbook/runbook.service.js.map +0 -1
  129. package/dist/src/shared/runbook/runbook.templates.d.ts +0 -6
  130. package/dist/src/shared/runbook/runbook.templates.js +0 -49
  131. package/dist/src/shared/runbook/runbook.templates.js.map +0 -1
  132. package/dist/src/shared/tokenizer/tokenizer.module.d.ts +0 -2
  133. package/dist/src/shared/tokenizer/tokenizer.module.js +0 -21
  134. package/dist/src/shared/tokenizer/tokenizer.module.js.map +0 -1
  135. package/dist/src/shared/tokenizer/tokenizer.service.d.ts +0 -10
  136. package/dist/src/shared/tokenizer/tokenizer.service.js +0 -36
  137. package/dist/src/shared/tokenizer/tokenizer.service.js.map +0 -1
  138. package/dist/tsconfig.build.tsbuildinfo +0 -1
  139. package/docs/todo.md +0 -7
  140. package/knip.json +0 -10
  141. package/kodu.json +0 -63
  142. package/kodu.schema.json +0 -100
  143. package/lefthook.yml +0 -11
  144. package/nest-cli.json +0 -8
  145. package/registry.schema.json +0 -39
  146. package/scripts/generate-json-schema.ts +0 -27
  147. package/skills/ac/SKILL.md +0 -239
  148. package/skills/al/SKILL.md +0 -98
  149. package/skills/audit/SKILL.md +0 -205
  150. package/skills/audit/audit-baseline-template.yml +0 -188
  151. package/skills/audit/runtime-detect.md +0 -64
  152. package/skills/audit/stacks/_generic.md +0 -41
  153. package/skills/audit/stacks/_registry.md +0 -47
  154. package/skills/audit/stacks/go.md +0 -66
  155. package/skills/audit/stacks/java.md +0 -44
  156. package/skills/audit/stacks/node.md +0 -57
  157. package/skills/audit/stacks/python.md +0 -45
  158. package/skills/audit/stacks/rust.md +0 -44
  159. package/skills/audit-api-contracts/SKILL.md +0 -201
  160. package/skills/audit-architecture/SKILL.md +0 -200
  161. package/skills/audit-bugs/SKILL.md +0 -226
  162. package/skills/audit-concurrency/SKILL.md +0 -197
  163. package/skills/audit-deployment/SKILL.md +0 -218
  164. package/skills/audit-docs/SKILL.md +0 -209
  165. package/skills/audit-errors/SKILL.md +0 -216
  166. package/skills/audit-logging/SKILL.md +0 -197
  167. package/skills/audit-matrix/SKILL.md +0 -245
  168. package/skills/audit-meta/SKILL.md +0 -120
  169. package/skills/audit-naming/SKILL.md +0 -200
  170. package/skills/audit-owasp/SKILL.md +0 -223
  171. package/skills/audit-performance/SKILL.md +0 -199
  172. package/skills/audit-reinvention/SKILL.md +0 -214
  173. package/skills/audit-secrets/SKILL.md +0 -198
  174. package/skills/audit-tests/SKILL.md +0 -210
  175. package/skills/audit-validation/SKILL.md +0 -206
  176. package/skills/audit-verify/SKILL.md +0 -139
  177. package/skills/audit-yagni/SKILL.md +0 -188
  178. package/skills/doc-gen/SKILL.md +0 -490
  179. package/skills/doc-gen/scripts/doc_gen.py +0 -911
  180. package/skills/generate-project-docs/SKILL.md +0 -380
  181. package/skills/implement-project/SKILL.md +0 -409
  182. package/skills/liteend-init/SKILL.md +0 -84
  183. package/skills/litefront-init/SKILL.md +0 -96
  184. package/skills/litefront-prototype/SKILL.md +0 -484
  185. package/skills/ops/SKILL.md +0 -94
  186. package/skills/post-call-task-builder/SKILL.md +0 -419
  187. package/skills/project-setup-standardizer/SKILL.md +0 -285
  188. package/skills/skills-best-practices/SKILL.md +0 -415
  189. package/skills/start/SKILL.md +0 -319
  190. package/skills/tech-blueprint/SKILL.md +0 -890
  191. package/skills/tech-blueprint/scripts/blueprint_validator.py +0 -417
  192. package/src/app.module.ts +0 -29
  193. package/src/commands/clean/clean.command.ts +0 -235
  194. package/src/commands/clean/clean.module.ts +0 -13
  195. package/src/commands/init/init.command.ts +0 -92
  196. package/src/commands/init/init.module.ts +0 -9
  197. package/src/commands/ops/ops-add.command.ts +0 -83
  198. package/src/commands/ops/ops-init.command.ts +0 -125
  199. package/src/commands/ops/ops-list.command.ts +0 -57
  200. package/src/commands/ops/ops-path.command.ts +0 -38
  201. package/src/commands/ops/ops-runbook.command.ts +0 -74
  202. package/src/commands/ops/ops-status.command.ts +0 -47
  203. package/src/commands/ops/ops-use.command.ts +0 -76
  204. package/src/commands/ops/ops.command.ts +0 -42
  205. package/src/commands/ops/ops.helpers.ts +0 -20
  206. package/src/commands/ops/ops.module.ts +0 -23
  207. package/src/commands/pack/pack.command.ts +0 -347
  208. package/src/commands/pack/pack.module.ts +0 -14
  209. package/src/core/config/config.module.ts +0 -10
  210. package/src/core/config/config.schema.ts +0 -58
  211. package/src/core/config/config.service.ts +0 -43
  212. package/src/core/config/prompt.service.ts +0 -80
  213. package/src/core/file-system/fs.module.ts +0 -8
  214. package/src/core/file-system/fs.service.ts +0 -248
  215. package/src/core/registry/registry.module.ts +0 -9
  216. package/src/core/registry/registry.schema.ts +0 -46
  217. package/src/core/registry/registry.service.ts +0 -128
  218. package/src/core/ui/ui.module.ts +0 -9
  219. package/src/core/ui/ui.service.ts +0 -39
  220. package/src/main.ts +0 -12
  221. package/src/shared/cleaner/cleaner.service.ts +0 -289
  222. package/src/shared/cleaner/cleaner.types.ts +0 -23
  223. package/src/shared/constants.ts +0 -118
  224. package/src/shared/deps/deps.module.ts +0 -8
  225. package/src/shared/deps/deps.service.ts +0 -175
  226. package/src/shared/git/git.module.ts +0 -8
  227. package/src/shared/git/git.service.ts +0 -47
  228. package/src/shared/runbook/runbook.module.ts +0 -9
  229. package/src/shared/runbook/runbook.service.ts +0 -164
  230. package/src/shared/runbook/runbook.templates.ts +0 -66
  231. package/src/shared/tokenizer/tokenizer.module.ts +0 -8
  232. package/src/shared/tokenizer/tokenizer.service.ts +0 -30
  233. package/tsconfig.build.json +0 -7
  234. package/tsconfig.json +0 -28
@@ -1,417 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- blueprint_validator.py — валидатор технических контрактов проекта.
4
-
5
- Использование:
6
- python3 blueprint_validator.py validate "ИмяПроекта" [--output PATH] [--update-mode]
7
- """
8
-
9
- import argparse
10
- import re
11
- import sys
12
- from pathlib import Path
13
-
14
-
15
- # ─── Цвета и вывод ────────────────────────────────────────────────────────────
16
-
17
- class C:
18
- RED = "\033[0;31m"
19
- GREEN = "\033[0;32m"
20
- YELLOW = "\033[1;33m"
21
- RESET = "\033[0m"
22
-
23
-
24
- def ok(msg: str) -> None: print(f"{C.GREEN}✓{C.RESET} {msg}")
25
- def err(msg: str) -> None: print(f"{C.RED}✗{C.RESET} {msg}")
26
- def warn(msg: str) -> None: print(f"{C.YELLOW}⟳{C.RESET} {msg}")
27
-
28
-
29
- # ─── Константы ────────────────────────────────────────────────────────────────
30
-
31
- REQUIRED_FILES = [
32
- "IMPLEMENTATION_GUIDE.md",
33
- "DATABASE_MODEL.md",
34
- "API_CONTRACTS.md",
35
- "ARCHITECTURE.md",
36
- "TESTING_PLAN.md",
37
- ]
38
-
39
- _FSD_PATH_RE = re.compile(
40
- r"src/(entities|features|widgets|pages|shared|app)/|"
41
- r"app/(entities|features|widgets|pages|shared)/"
42
- )
43
-
44
- _LIST_TYPE_RE = re.compile(r":\s*\[\w")
45
- _PAGINATION_KW_RE = re.compile(r"\b(first|after|limit|offset|page|cursor)\b", re.IGNORECASE)
46
- _SPEC_REF_RE = re.compile(r"(SPEC\.md|VISION\.md)")
47
- _PRISMA_MODEL_RE = re.compile(r"model\s+(\w+)\s*\{([\s\S]*?)\n\}", re.MULTILINE)
48
- _MIGRATION_SEC_RE = re.compile(
49
- r"^#{1,3}\s*(план миграции|изменения бд|migration plan|db changes|database changes)",
50
- re.MULTILINE | re.IGNORECASE,
51
- )
52
- _IMPL_STACK_RE = re.compile(r"^#{1,3}\s*(стек|stack)\b", re.MULTILINE | re.IGNORECASE)
53
- _IMPL_DONE_RE = re.compile(
54
- r"^#{1,3}\s*(что уже реализовано|already implemented|what.s already)",
55
- re.MULTILINE | re.IGNORECASE,
56
- )
57
- _IMPL_LAUNCH_RE = re.compile(
58
- r"^#{1,3}\s*(локальный запуск|local (setup|run|start)|getting started|quick start)",
59
- re.MULTILINE | re.IGNORECASE,
60
- )
61
-
62
-
63
- # ─── Вспомогательные функции ──────────────────────────────────────────────────
64
-
65
- def _read(path: Path) -> str:
66
- return path.read_text(encoding="utf-8")
67
-
68
-
69
- def _extract_code_block(content: str, lang: str) -> str:
70
- """Возвращает содержимое первого блока ```lang ... ```."""
71
- m = re.search(rf"```{re.escape(lang)}\n([\s\S]*?)```", content)
72
- return m.group(1) if m else ""
73
-
74
-
75
- def _extract_prisma_models(prisma_content: str) -> dict[str, str]:
76
- """Возвращает {ИмяМодели: тело_модели} из Prisma-схемы."""
77
- return {m.group(1): m.group(2) for m in _PRISMA_MODEL_RE.finditer(prisma_content)}
78
-
79
-
80
- # ─── Проверки ─────────────────────────────────────────────────────────────────
81
-
82
- def check_files(blueprint_dir: Path) -> list[str]:
83
- """Проверка 1: все обязательные файлы существуют."""
84
- return [
85
- f"Файл отсутствует: {f}"
86
- for f in REQUIRED_FILES
87
- if not (blueprint_dir / f).exists()
88
- ]
89
-
90
-
91
- def check_code_blocks(blueprint_dir: Path) -> list[str]:
92
- """Проверка 2: DATABASE_MODEL.md содержит ```prisma, API_CONTRACTS.md — ```graphql."""
93
- errors: list[str] = []
94
- db = _read(blueprint_dir / "DATABASE_MODEL.md")
95
- if not _extract_code_block(db, "prisma"):
96
- errors.append("DATABASE_MODEL.md: отсутствует блок ```prisma")
97
- api = _read(blueprint_dir / "API_CONTRACTS.md")
98
- if not _extract_code_block(api, "graphql"):
99
- errors.append("API_CONTRACTS.md: отсутствует блок ```graphql")
100
- return errors
101
-
102
-
103
- def check_fsd_paths(blueprint_dir: Path) -> list[str]:
104
- """Проверка 3: ARCHITECTURE.md не содержит файловых путей FSD."""
105
- arch = _read(blueprint_dir / "ARCHITECTURE.md")
106
- matches = _FSD_PATH_RE.findall(arch)
107
- if matches:
108
- return [
109
- "ARCHITECTURE.md: обнаружены файловые пути FSD. "
110
- "В архитектурном документе должны быть только логические названия "
111
- "сущностей и компонентов (не файловые пути)."
112
- ]
113
- return []
114
-
115
-
116
- def check_model_coverage(blueprint_dir: Path) -> list[str]:
117
- """Проверка 4: большинство Prisma-моделей упомянуты в API_CONTRACTS.md."""
118
- db = _read(blueprint_dir / "DATABASE_MODEL.md")
119
- api = _read(blueprint_dir / "API_CONTRACTS.md")
120
-
121
- prisma_block = _extract_code_block(db, "prisma")
122
- if not prisma_block:
123
- return []
124
-
125
- model_names = list(_extract_prisma_models(prisma_block).keys())
126
- if not model_names:
127
- return []
128
-
129
- api_lower = api.lower()
130
- covered = [m for m in model_names if m.lower() in api_lower]
131
- ratio = len(covered) / len(model_names)
132
-
133
- if ratio < 0.5:
134
- missing = [m for m in model_names if m.lower() not in api_lower]
135
- return [
136
- f"Кросс-чек БД/API: {len(covered)}/{len(model_names)} Prisma-моделей "
137
- f"найдены в API_CONTRACTS.md. Отсутствуют: {', '.join(missing)}"
138
- ]
139
- return []
140
-
141
-
142
- def check_traceability(blueprint_dir: Path) -> list[str]:
143
- """Проверка 5: DATABASE_MODEL.md и API_CONTRACTS.md содержат ссылки на SPEC.md/VISION.md."""
144
- errors: list[str] = []
145
- for filename in ("DATABASE_MODEL.md", "API_CONTRACTS.md"):
146
- content = _read(blueprint_dir / filename)
147
- if not _SPEC_REF_RE.search(content):
148
- errors.append(
149
- f"{filename}: отсутствует трассируемость. "
150
- "Добавьте комментарии со ссылками на бизнес-требования (SPEC.md или VISION.md)"
151
- )
152
- return errors
153
-
154
-
155
- def check_pagination(blueprint_dir: Path) -> list[str]:
156
- """
157
- Проверка 6: поля Query/Mutation, возвращающие списки, имеют аргументы пагинации.
158
- Ищет только внутри type Query / type Mutation / type Subscription.
159
- Предупреждения, не ошибки (pagination может быть в обёртке-типе).
160
- """
161
- issues: list[str] = []
162
- api = _read(blueprint_dir / "API_CONTRACTS.md")
163
- graphql_block = _extract_code_block(api, "graphql")
164
- if not graphql_block:
165
- return issues
166
-
167
- lines = graphql_block.splitlines()
168
- in_root_op = False
169
- current_type: str | None = None
170
- depth = 0
171
-
172
- for i, line in enumerate(lines):
173
- stripped = line.strip()
174
-
175
- # Определить вход в тип Query/Mutation/Subscription
176
- m = re.match(r"^type\s+(Query|Mutation|Subscription)\b", stripped)
177
- if m:
178
- in_root_op = True
179
- current_type = m.group(1)
180
- depth = 0
181
-
182
- # Обновить глубину вложенности
183
- depth += stripped.count("{") - stripped.count("}")
184
- if depth <= 0 and in_root_op:
185
- in_root_op = False
186
- current_type = None
187
- continue
188
-
189
- if not in_root_op:
190
- continue
191
-
192
- # Проверить, возвращает ли строка список
193
- if not _LIST_TYPE_RE.search(stripped):
194
- continue
195
-
196
- # Собрать контекст: до 8 предыдущих строк (для многострочных аргументов)
197
- ctx = "\n".join(lines[max(0, i - 8) : i + 1])
198
- if not _PAGINATION_KW_RE.search(ctx):
199
- name_m = re.search(r"(\w+)\s*[\(:]", stripped)
200
- field_name = name_m.group(1) if name_m else stripped[:50]
201
- issues.append(
202
- f"API_CONTRACTS.md [{current_type}]: поле «{field_name}» возвращает список "
203
- "без аргументов пагинации — добавьте first/after или limit/offset"
204
- )
205
-
206
- return issues
207
-
208
-
209
- def check_prisma_timestamps(blueprint_dir: Path) -> list[str]:
210
- """
211
- Проверка 7: каждая Prisma-модель (кроме join-таблиц с '_' в названии)
212
- содержит createdAt или updatedAt.
213
- """
214
- errors: list[str] = []
215
- db = _read(blueprint_dir / "DATABASE_MODEL.md")
216
- prisma_block = _extract_code_block(db, "prisma")
217
- if not prisma_block:
218
- return errors
219
-
220
- for model_name, body in _extract_prisma_models(prisma_block).items():
221
- # Join-таблицы (содержат '_' в имени) — пропустить
222
- if "_" in model_name:
223
- continue
224
- if "createdAt" not in body and "updatedAt" not in body:
225
- errors.append(
226
- f"DATABASE_MODEL.md: модель «{model_name}» не содержит "
227
- "обязательных полей createdAt/updatedAt"
228
- )
229
-
230
- return errors
231
-
232
-
233
- def check_implementation_guide(blueprint_dir: Path) -> list[str]:
234
- """Проверка 8: IMPLEMENTATION_GUIDE.md содержит обязательные разделы."""
235
- guide_path = blueprint_dir / "IMPLEMENTATION_GUIDE.md"
236
- if not guide_path.exists():
237
- return [] # уже зафиксировано в check_files
238
- content = _read(guide_path)
239
- errors: list[str] = []
240
- if not _IMPL_STACK_RE.search(content):
241
- errors.append("IMPLEMENTATION_GUIDE.md: отсутствует раздел «## Стек»")
242
- if not _IMPL_DONE_RE.search(content):
243
- errors.append("IMPLEMENTATION_GUIDE.md: отсутствует раздел «## Что уже реализовано»")
244
- if not _IMPL_LAUNCH_RE.search(content):
245
- errors.append("IMPLEMENTATION_GUIDE.md: отсутствует раздел «## Локальный запуск»")
246
- return errors
247
-
248
-
249
- def check_migration_section(blueprint_dir: Path) -> list[str]:
250
- """Проверка 8 (--update-mode): DATABASE_MODEL.md содержит раздел «План миграции»."""
251
- db = _read(blueprint_dir / "DATABASE_MODEL.md")
252
- if not _MIGRATION_SEC_RE.search(db):
253
- return [
254
- "DATABASE_MODEL.md: в режиме обновления требуется описать план миграции. "
255
- "Добавьте раздел «## План миграции» или «## Изменения БД»"
256
- ]
257
- return []
258
-
259
-
260
- # ─── Команда validate ──────────────────────────────────────────────────────────
261
-
262
- def cmd_validate(args: argparse.Namespace) -> int:
263
- blueprint_dir = Path(args.output) / args.name / "3_TECH_BLUEPRINT"
264
-
265
- if not blueprint_dir.exists():
266
- err(f"Папка не найдена: {blueprint_dir}")
267
- print(f" Ожидается: docs/{args.name}/3_TECH_BLUEPRINT/")
268
- return 1
269
-
270
- print(f"\nВалидация технического блюпринта: {args.name}\n")
271
-
272
- all_errors: list[str] = []
273
- all_warnings: list[str] = []
274
-
275
- # ── 1. Наличие файлов ─────────────────────────────────────────────────────
276
- print(f"{C.YELLOW}── Наличие файлов ──────────────────────────{C.RESET}")
277
- file_errors = check_files(blueprint_dir)
278
- for e in file_errors:
279
- err(e)
280
- if file_errors:
281
- print(
282
- f"\n{C.RED}Критические ошибки: создайте все обязательные файлы "
283
- f"прежде чем продолжить.{C.RESET}\n"
284
- )
285
- return 1
286
- ok("Все обязательные файлы присутствуют")
287
-
288
- # ── 2. Блоки кода ─────────────────────────────────────────────────────────
289
- print(f"\n{C.YELLOW}── Структура контента ──────────────────────{C.RESET}")
290
- block_errors = check_code_blocks(blueprint_dir)
291
- for e in block_errors:
292
- err(e)
293
- all_errors.extend(block_errors)
294
- if not block_errors:
295
- ok("Блоки кода (```prisma, ```graphql) найдены")
296
-
297
- # ── 3. FSD-пути ───────────────────────────────────────────────────────────
298
- print(f"\n{C.YELLOW}── FSD Paths Check ─────────────────────────{C.RESET}")
299
- fsd_errors = check_fsd_paths(blueprint_dir)
300
- for e in fsd_errors:
301
- err(e)
302
- all_errors.extend(fsd_errors)
303
- if not fsd_errors:
304
- ok("ARCHITECTURE.md не содержит файловых путей FSD")
305
-
306
- # ── 4. Кросс-чек моделей ──────────────────────────────────────────────────
307
- print(f"\n{C.YELLOW}── Кросс-чек БД / GraphQL ──────────────────{C.RESET}")
308
- if not block_errors:
309
- coverage_errors = check_model_coverage(blueprint_dir)
310
- for e in coverage_errors:
311
- err(e)
312
- all_errors.extend(coverage_errors)
313
- if not coverage_errors:
314
- ok("Большинство Prisma-моделей упомянуты в API_CONTRACTS.md")
315
-
316
- # ── 5. Трассируемость ─────────────────────────────────────────────────────
317
- print(f"\n{C.YELLOW}── Трассируемость ──────────────────────────{C.RESET}")
318
- trace_errors = check_traceability(blueprint_dir)
319
- for e in trace_errors:
320
- err(e)
321
- all_errors.extend(trace_errors)
322
- if not trace_errors:
323
- ok("Ссылки на бизнес-требования найдены в DB-модели и API-контрактах")
324
-
325
- # ── 6. Пагинация GraphQL ──────────────────────────────────────────────────
326
- print(f"\n{C.YELLOW}── Пагинация GraphQL ───────────────────────{C.RESET}")
327
- if not block_errors:
328
- pag_issues = check_pagination(blueprint_dir)
329
- for issue in pag_issues:
330
- warn(issue)
331
- all_warnings.extend(pag_issues)
332
- if not pag_issues:
333
- ok("Все списочные поля Query/Mutation имеют аргументы пагинации")
334
-
335
- # ── 7. Технические поля Prisma ────────────────────────────────────────────
336
- print(f"\n{C.YELLOW}── Технические поля Prisma ─────────────────{C.RESET}")
337
- if not block_errors:
338
- ts_errors = check_prisma_timestamps(blueprint_dir)
339
- for e in ts_errors:
340
- err(e)
341
- all_errors.extend(ts_errors)
342
- if not ts_errors:
343
- ok("Все модели (кроме join-таблиц) содержат createdAt/updatedAt")
344
-
345
- # ── 8. IMPLEMENTATION_GUIDE.md ────────────────────────────────────────────
346
- print(f"\n{C.YELLOW}── IMPLEMENTATION_GUIDE.md ─────────────────{C.RESET}")
347
- guide_errors = check_implementation_guide(blueprint_dir)
348
- for e in guide_errors:
349
- err(e)
350
- all_errors.extend(guide_errors)
351
- if not guide_errors:
352
- ok("IMPLEMENTATION_GUIDE.md содержит обязательные разделы")
353
-
354
- # ── 9. Режим обновления ───────────────────────────────────────────────────
355
- if args.update_mode:
356
- print(f"\n{C.YELLOW}── Режим обновления (миграция) ─────────────{C.RESET}")
357
- mig_errors = check_migration_section(blueprint_dir)
358
- for e in mig_errors:
359
- err(e)
360
- all_errors.extend(mig_errors)
361
- if not mig_errors:
362
- ok("Раздел с планом миграции найден")
363
-
364
- # ── Итог ──────────────────────────────────────────────────────────────────
365
- print()
366
- if all_errors:
367
- warn_suffix = f", предупреждений: {len(all_warnings)}" if all_warnings else ""
368
- print(
369
- f"{C.RED}Итог: {len(all_errors)} ошибок{warn_suffix}. "
370
- f"Устраните ошибки перед продолжением.{C.RESET}\n"
371
- )
372
- return 1
373
-
374
- if all_warnings:
375
- print(
376
- f"{C.YELLOW}Итог: ошибок нет, предупреждений: {len(all_warnings)}. "
377
- f"Рекомендуется исправить.{C.RESET}\n"
378
- )
379
- else:
380
- print(
381
- f"{C.GREEN}✅ Блюпринт «{args.name}» прошёл полную проверку.{C.RESET}\n"
382
- )
383
-
384
- return 0
385
-
386
-
387
- # ─── CLI ──────────────────────────────────────────────────────────────────────
388
-
389
- def build_parser() -> argparse.ArgumentParser:
390
- parser = argparse.ArgumentParser(
391
- prog="blueprint_validator.py",
392
- description="Валидатор технических контрактов (tech-blueprint)",
393
- )
394
- sub = parser.add_subparsers(dest="command", required=True)
395
-
396
- val = sub.add_parser("validate", help="Проверить папку 3_TECH_BLUEPRINT/")
397
- val.add_argument("name", metavar="ИмяПроекта")
398
- val.add_argument(
399
- "--output", default="./blueprint", metavar="PATH",
400
- help="Корневая папка проектов (по умолчанию: ./blueprint)",
401
- )
402
- val.add_argument(
403
- "--update-mode", action="store_true",
404
- help="Режим обновления: требует раздел «План миграции» в DATABASE_MODEL.md",
405
- )
406
-
407
- return parser
408
-
409
-
410
- def main() -> None:
411
- parser = build_parser()
412
- args = parser.parse_args()
413
- sys.exit(cmd_validate(args))
414
-
415
-
416
- if __name__ == "__main__":
417
- main()
package/src/app.module.ts DELETED
@@ -1,29 +0,0 @@
1
- import { Module } from '@nestjs/common';
2
- import { CleanModule } from './commands/clean/clean.module';
3
- import { InitModule } from './commands/init/init.module';
4
- import { OpsModule } from './commands/ops/ops.module';
5
- import { PackModule } from './commands/pack/pack.module';
6
- import { ConfigModule } from './core/config/config.module';
7
- import { FsModule } from './core/file-system/fs.module';
8
- import { RegistryModule } from './core/registry/registry.module';
9
- import { UiModule } from './core/ui/ui.module';
10
- import { GitModule } from './shared/git/git.module';
11
- import { RunbookModule } from './shared/runbook/runbook.module';
12
- import { TokenizerModule } from './shared/tokenizer/tokenizer.module';
13
-
14
- @Module({
15
- imports: [
16
- ConfigModule,
17
- UiModule,
18
- FsModule,
19
- GitModule,
20
- TokenizerModule,
21
- RegistryModule,
22
- RunbookModule,
23
- InitModule,
24
- PackModule,
25
- CleanModule,
26
- OpsModule,
27
- ],
28
- })
29
- export class AppModule {}
@@ -1,235 +0,0 @@
1
- import { createReadStream } from 'node:fs';
2
- import { Command, CommandRunner, Option } from 'nest-commander';
3
- import { ConfigService } from '../../core/config/config.service';
4
- import { FsService } from '../../core/file-system/fs.service';
5
- import { UiService } from '../../core/ui/ui.service';
6
- import { CleanerService } from '../../shared/cleaner/cleaner.service';
7
- import { GitService } from '../../shared/git/git.service';
8
-
9
- const SUPPORTED_EXTENSIONS = /\.(ts|tsx|js|jsx|mjs|cjs|html|htm)$/i;
10
-
11
- type CleanOptions = {
12
- dryRun?: boolean;
13
- changed?: boolean;
14
- staged?: boolean;
15
- backup?: boolean;
16
- noJsdoc?: boolean;
17
- verbose?: boolean;
18
- stdin?: boolean;
19
- };
20
-
21
- @Command({ name: 'clean', description: 'Remove comments from code' })
22
- export class CleanCommand extends CommandRunner {
23
- constructor(
24
- private readonly ui: UiService,
25
- private readonly fsService: FsService,
26
- private readonly cleaner: CleanerService,
27
- private readonly config: ConfigService,
28
- private readonly git: GitService,
29
- ) {
30
- super();
31
- }
32
-
33
- @Option({ flags: '-d, --dry-run', description: 'Show what will be removed' })
34
- parseDryRun(): boolean {
35
- return true;
36
- }
37
-
38
- @Option({
39
- flags: '-c, --changed',
40
- description: 'Clean only git-changed files (staged + unstaged + untracked)',
41
- })
42
- parseChanged(): boolean {
43
- return true;
44
- }
45
-
46
- @Option({
47
- flags: '-s, --staged',
48
- description: 'Clean only git-staged files',
49
- })
50
- parseStaged(): boolean {
51
- return true;
52
- }
53
-
54
- @Option({
55
- flags: '-b, --backup',
56
- description: 'Save originals to .kodu/backup/ before modifying',
57
- })
58
- parseBackup(): boolean {
59
- return true;
60
- }
61
-
62
- @Option({
63
- flags: '-n, --no-jsdoc',
64
- description: 'Remove JSDoc comments (overrides config keepJSDoc)',
65
- })
66
- parseNoJsdoc(): boolean {
67
- return true;
68
- }
69
-
70
- @Option({
71
- flags: '-v, --verbose',
72
- description: 'Show all removed comments in dry-run (not just first 3)',
73
- })
74
- parseVerbose(): boolean {
75
- return true;
76
- }
77
-
78
- @Option({
79
- flags: '--stdin',
80
- description: 'Read from stdin, write cleaned result to stdout',
81
- })
82
- parseStdin(): boolean {
83
- return true;
84
- }
85
-
86
- async run(inputs: string[], options: CleanOptions = {}): Promise<void> {
87
- if (options.stdin) {
88
- await this.runStdin(options);
89
- return;
90
- }
91
-
92
- const spinner = this.ui
93
- .createSpinner({ text: this.buildSpinnerText(options) })
94
- .start();
95
-
96
- try {
97
- const { cleaner: cleanerConfig, packer } = this.config.getConfig();
98
- const ignorePatterns = [
99
- ...(packer.ignore ?? []),
100
- ...(cleanerConfig.ignore ?? []),
101
- ];
102
- const allFiles = await this.fsService.findProjectFiles({
103
- useGitignore: cleanerConfig.useGitignore,
104
- ignore: ignorePatterns,
105
- });
106
-
107
- const targets = await this.collectTargets(allFiles, inputs, options);
108
-
109
- if (targets.length === 0) {
110
- const msg = this.noFilesMessage(options);
111
- spinner.stop(msg);
112
- this.ui.log.warn(msg);
113
- return;
114
- }
115
-
116
- const summary = await this.cleaner.cleanFiles(targets, {
117
- dryRun: options.dryRun,
118
- backup: options.backup,
119
- keepJSDoc: options.noJsdoc ? false : undefined,
120
- onProgress: (current, total) => {
121
- spinner.text = `${this.buildSpinnerText(options)} (${current}/${total})`;
122
- },
123
- });
124
-
125
- spinner.success(
126
- options.dryRun ? 'Analysis complete' : 'Cleaning complete',
127
- );
128
-
129
- const bytesSaved = summary.bytesBefore - summary.bytesAfter;
130
- const tokensSaved = Math.round(bytesSaved / 4);
131
-
132
- if (options.dryRun) {
133
- this.ui.log.info(
134
- `Files affected: ${summary.filesChanged}/${summary.filesProcessed}, comments: ${summary.commentsRemoved}`,
135
- );
136
- this.ui.log.info(`Bytes saved: ${bytesSaved} (~${tokensSaved} tokens)`);
137
-
138
- const limit = options.verbose ? Number.POSITIVE_INFINITY : 3;
139
- for (const report of summary.reports.filter((r) => r.removed > 0)) {
140
- const previews = options.verbose
141
- ? report.previews
142
- : report.previews.slice(0, limit);
143
- const more =
144
- !options.verbose && report.previews.length > limit
145
- ? ` +${report.previews.length - limit} more`
146
- : '';
147
- this.ui.log.info(
148
- ` ${report.file} (${report.removed}): ${previews.map((p) => `"${p}"`).join(', ')}${more}`,
149
- );
150
- }
151
- return;
152
- }
153
-
154
- this.ui.log.success(
155
- `Files cleaned: ${summary.filesChanged}, comments removed: ${summary.commentsRemoved}`,
156
- );
157
- this.ui.log.info(`Bytes saved: ${bytesSaved} (~${tokensSaved} tokens)`);
158
-
159
- if (options.backup && summary.filesChanged > 0) {
160
- this.ui.log.info('Originals backed up to .kodu/backup/');
161
- }
162
- } catch (error) {
163
- spinner.error('Error during cleaning');
164
- const message = error instanceof Error ? error.message : 'Unknown error';
165
- this.ui.log.error(message);
166
- process.exitCode = 1;
167
- }
168
- }
169
-
170
- private async runStdin(options: CleanOptions): Promise<void> {
171
- try {
172
- const input = await this.readStdin();
173
- const cleaned = this.cleaner.cleanContent(
174
- 'stdin.ts',
175
- input,
176
- options.noJsdoc ? false : undefined,
177
- );
178
- process.stdout.write(cleaned);
179
- } catch (error) {
180
- const message = error instanceof Error ? error.message : 'Unknown error';
181
- this.ui.log.error(message);
182
- process.exitCode = 1;
183
- }
184
- }
185
-
186
- private readStdin(): Promise<string> {
187
- return new Promise((resolve, reject) => {
188
- const chunks: Buffer[] = [];
189
- const stream = createReadStream('/dev/stdin');
190
- stream.on('data', (chunk) =>
191
- chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk),
192
- );
193
- stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
194
- stream.on('error', reject);
195
- });
196
- }
197
-
198
- private buildSpinnerText(options: CleanOptions): string {
199
- if (options.staged) return 'Cleaning staged files...';
200
- if (options.changed) return 'Cleaning changed files...';
201
- return options.dryRun ? 'Analysing...' : 'Cleaning...';
202
- }
203
-
204
- private noFilesMessage(options: CleanOptions): string {
205
- if (options.staged) return 'No staged files to clean.';
206
- if (options.changed) return 'No changed files to clean.';
207
- return 'No files to clean.';
208
- }
209
-
210
- private async collectTargets(
211
- allFiles: string[],
212
- inputs: string[],
213
- options: CleanOptions,
214
- ): Promise<string[]> {
215
- const supported = allFiles.filter((f) => SUPPORTED_EXTENSIONS.test(f));
216
-
217
- if (inputs.length > 0) {
218
- return supported.filter((f) =>
219
- inputs.some((i) => f === i || f.startsWith(`${i.replace(/\/$/, '')}/`)),
220
- );
221
- }
222
-
223
- if (options.staged) {
224
- const staged = new Set(await this.git.getStagedFiles());
225
- return supported.filter((f) => staged.has(f));
226
- }
227
-
228
- if (options.changed) {
229
- const changed = new Set(await this.git.getChangedFiles());
230
- return supported.filter((f) => changed.has(f));
231
- }
232
-
233
- return supported;
234
- }
235
- }