kodu 2.2.0 → 3.0.1

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 (233) hide show
  1. package/README.md +24 -3
  2. package/bin/kodu.js +23 -0
  3. package/package.json +11 -67
  4. package/scripts/postinstall.js +69 -0
  5. package/AGENTS.md +0 -214
  6. package/__tests__/core/fs/fs.service.test.ts +0 -72
  7. package/__tests__/core/registry/registry.service.test.ts +0 -82
  8. package/__tests__/shared/cleaner/cleaner.service.test.ts +0 -102
  9. package/__tests__/shared/git/git.service.test.ts +0 -84
  10. package/__tests__/shared/runbook/runbook.service.test.ts +0 -104
  11. package/__tests__/shared/tokenizer/tokenizer.service.test.ts +0 -45
  12. package/biome.json +0 -50
  13. package/dist/package.json +0 -96
  14. package/dist/src/app.module.d.ts +0 -2
  15. package/dist/src/app.module.js +0 -42
  16. package/dist/src/app.module.js.map +0 -1
  17. package/dist/src/commands/clean/clean.command.d.ts +0 -37
  18. package/dist/src/commands/clean/clean.command.js +0 -240
  19. package/dist/src/commands/clean/clean.command.js.map +0 -1
  20. package/dist/src/commands/clean/clean.module.d.ts +0 -2
  21. package/dist/src/commands/clean/clean.module.js +0 -26
  22. package/dist/src/commands/clean/clean.module.js.map +0 -1
  23. package/dist/src/commands/init/init.command.d.ts +0 -10
  24. package/dist/src/commands/init/init.command.js +0 -96
  25. package/dist/src/commands/init/init.command.js.map +0 -1
  26. package/dist/src/commands/init/init.module.d.ts +0 -2
  27. package/dist/src/commands/init/init.module.js +0 -22
  28. package/dist/src/commands/init/init.module.js.map +0 -1
  29. package/dist/src/commands/ops/ops-add.command.d.ts +0 -18
  30. package/dist/src/commands/ops/ops-add.command.js +0 -102
  31. package/dist/src/commands/ops/ops-add.command.js.map +0 -1
  32. package/dist/src/commands/ops/ops-init.command.d.ts +0 -22
  33. package/dist/src/commands/ops/ops-init.command.js +0 -130
  34. package/dist/src/commands/ops/ops-init.command.js.map +0 -1
  35. package/dist/src/commands/ops/ops-list.command.d.ts +0 -12
  36. package/dist/src/commands/ops/ops-list.command.js +0 -73
  37. package/dist/src/commands/ops/ops-list.command.js.map +0 -1
  38. package/dist/src/commands/ops/ops-path.command.d.ts +0 -9
  39. package/dist/src/commands/ops/ops-path.command.js +0 -52
  40. package/dist/src/commands/ops/ops-path.command.js.map +0 -1
  41. package/dist/src/commands/ops/ops-runbook.command.d.ts +0 -12
  42. package/dist/src/commands/ops/ops-runbook.command.js +0 -81
  43. package/dist/src/commands/ops/ops-runbook.command.js.map +0 -1
  44. package/dist/src/commands/ops/ops-status.command.d.ts +0 -11
  45. package/dist/src/commands/ops/ops-status.command.js +0 -62
  46. package/dist/src/commands/ops/ops-status.command.js.map +0 -1
  47. package/dist/src/commands/ops/ops-use.command.d.ts +0 -12
  48. package/dist/src/commands/ops/ops-use.command.js +0 -76
  49. package/dist/src/commands/ops/ops-use.command.js.map +0 -1
  50. package/dist/src/commands/ops/ops.command.d.ts +0 -7
  51. package/dist/src/commands/ops/ops.command.js +0 -56
  52. package/dist/src/commands/ops/ops.command.js.map +0 -1
  53. package/dist/src/commands/ops/ops.helpers.d.ts +0 -2
  54. package/dist/src/commands/ops/ops.helpers.js +0 -11
  55. package/dist/src/commands/ops/ops.helpers.js.map +0 -1
  56. package/dist/src/commands/ops/ops.module.d.ts +0 -2
  57. package/dist/src/commands/ops/ops.module.js +0 -36
  58. package/dist/src/commands/ops/ops.module.js.map +0 -1
  59. package/dist/src/commands/pack/pack.command.d.ts +0 -51
  60. package/dist/src/commands/pack/pack.command.js +0 -355
  61. package/dist/src/commands/pack/pack.command.js.map +0 -1
  62. package/dist/src/commands/pack/pack.module.d.ts +0 -2
  63. package/dist/src/commands/pack/pack.module.js +0 -27
  64. package/dist/src/commands/pack/pack.module.js.map +0 -1
  65. package/dist/src/core/config/config.module.d.ts +0 -2
  66. package/dist/src/core/config/config.module.js +0 -23
  67. package/dist/src/core/config/config.module.js.map +0 -1
  68. package/dist/src/core/config/config.schema.d.ts +0 -19
  69. package/dist/src/core/config/config.schema.js +0 -56
  70. package/dist/src/core/config/config.schema.js.map +0 -1
  71. package/dist/src/core/config/config.service.d.ts +0 -7
  72. package/dist/src/core/config/config.service.js +0 -49
  73. package/dist/src/core/config/config.service.js.map +0 -1
  74. package/dist/src/core/config/prompt.service.d.ts +0 -10
  75. package/dist/src/core/config/prompt.service.js +0 -80
  76. package/dist/src/core/config/prompt.service.js.map +0 -1
  77. package/dist/src/core/file-system/fs.module.d.ts +0 -2
  78. package/dist/src/core/file-system/fs.module.js +0 -21
  79. package/dist/src/core/file-system/fs.module.js.map +0 -1
  80. package/dist/src/core/file-system/fs.service.d.ts +0 -27
  81. package/dist/src/core/file-system/fs.service.js +0 -203
  82. package/dist/src/core/file-system/fs.service.js.map +0 -1
  83. package/dist/src/core/registry/registry.module.d.ts +0 -2
  84. package/dist/src/core/registry/registry.module.js +0 -22
  85. package/dist/src/core/registry/registry.module.js.map +0 -1
  86. package/dist/src/core/registry/registry.schema.d.ts +0 -24
  87. package/dist/src/core/registry/registry.schema.js +0 -21
  88. package/dist/src/core/registry/registry.schema.js.map +0 -1
  89. package/dist/src/core/registry/registry.service.d.ts +0 -16
  90. package/dist/src/core/registry/registry.service.js +0 -91
  91. package/dist/src/core/registry/registry.service.js.map +0 -1
  92. package/dist/src/core/ui/ui.module.d.ts +0 -2
  93. package/dist/src/core/ui/ui.module.js +0 -22
  94. package/dist/src/core/ui/ui.module.js.map +0 -1
  95. package/dist/src/core/ui/ui.service.d.ts +0 -22
  96. package/dist/src/core/ui/ui.service.js +0 -43
  97. package/dist/src/core/ui/ui.service.js.map +0 -1
  98. package/dist/src/main.d.ts +0 -2
  99. package/dist/src/main.js +0 -16
  100. package/dist/src/main.js.map +0 -1
  101. package/dist/src/shared/cleaner/cleaner.service.d.ts +0 -23
  102. package/dist/src/shared/cleaner/cleaner.service.js +0 -223
  103. package/dist/src/shared/cleaner/cleaner.service.js.map +0 -1
  104. package/dist/src/shared/cleaner/cleaner.types.d.ts +0 -21
  105. package/dist/src/shared/cleaner/cleaner.types.js +0 -3
  106. package/dist/src/shared/cleaner/cleaner.types.js.map +0 -1
  107. package/dist/src/shared/constants.d.ts +0 -4
  108. package/dist/src/shared/constants.js +0 -113
  109. package/dist/src/shared/constants.js.map +0 -1
  110. package/dist/src/shared/deps/deps.module.d.ts +0 -2
  111. package/dist/src/shared/deps/deps.module.js +0 -21
  112. package/dist/src/shared/deps/deps.module.js.map +0 -1
  113. package/dist/src/shared/deps/deps.service.d.ts +0 -15
  114. package/dist/src/shared/deps/deps.service.js +0 -114
  115. package/dist/src/shared/deps/deps.service.js.map +0 -1
  116. package/dist/src/shared/git/git.module.d.ts +0 -2
  117. package/dist/src/shared/git/git.module.js +0 -21
  118. package/dist/src/shared/git/git.module.js.map +0 -1
  119. package/dist/src/shared/git/git.service.d.ts +0 -5
  120. package/dist/src/shared/git/git.service.js +0 -56
  121. package/dist/src/shared/git/git.service.js.map +0 -1
  122. package/dist/src/shared/runbook/runbook.module.d.ts +0 -2
  123. package/dist/src/shared/runbook/runbook.module.js +0 -22
  124. package/dist/src/shared/runbook/runbook.module.js.map +0 -1
  125. package/dist/src/shared/runbook/runbook.service.d.ts +0 -20
  126. package/dist/src/shared/runbook/runbook.service.js +0 -118
  127. package/dist/src/shared/runbook/runbook.service.js.map +0 -1
  128. package/dist/src/shared/runbook/runbook.templates.d.ts +0 -6
  129. package/dist/src/shared/runbook/runbook.templates.js +0 -49
  130. package/dist/src/shared/runbook/runbook.templates.js.map +0 -1
  131. package/dist/src/shared/tokenizer/tokenizer.module.d.ts +0 -2
  132. package/dist/src/shared/tokenizer/tokenizer.module.js +0 -21
  133. package/dist/src/shared/tokenizer/tokenizer.module.js.map +0 -1
  134. package/dist/src/shared/tokenizer/tokenizer.service.d.ts +0 -10
  135. package/dist/src/shared/tokenizer/tokenizer.service.js +0 -36
  136. package/dist/src/shared/tokenizer/tokenizer.service.js.map +0 -1
  137. package/dist/tsconfig.build.tsbuildinfo +0 -1
  138. package/docs/todo.md +0 -7
  139. package/knip.json +0 -10
  140. package/kodu.json +0 -63
  141. package/kodu.schema.json +0 -100
  142. package/lefthook.yml +0 -11
  143. package/nest-cli.json +0 -8
  144. package/registry.schema.json +0 -39
  145. package/scripts/generate-json-schema.ts +0 -27
  146. package/skills/ac/SKILL.md +0 -239
  147. package/skills/al/SKILL.md +0 -98
  148. package/skills/audit/SKILL.md +0 -205
  149. package/skills/audit/audit-baseline-template.yml +0 -188
  150. package/skills/audit/runtime-detect.md +0 -64
  151. package/skills/audit/stacks/_generic.md +0 -41
  152. package/skills/audit/stacks/_registry.md +0 -47
  153. package/skills/audit/stacks/go.md +0 -66
  154. package/skills/audit/stacks/java.md +0 -44
  155. package/skills/audit/stacks/node.md +0 -57
  156. package/skills/audit/stacks/python.md +0 -45
  157. package/skills/audit/stacks/rust.md +0 -44
  158. package/skills/audit-api-contracts/SKILL.md +0 -201
  159. package/skills/audit-architecture/SKILL.md +0 -200
  160. package/skills/audit-bugs/SKILL.md +0 -226
  161. package/skills/audit-concurrency/SKILL.md +0 -197
  162. package/skills/audit-deployment/SKILL.md +0 -218
  163. package/skills/audit-docs/SKILL.md +0 -209
  164. package/skills/audit-errors/SKILL.md +0 -216
  165. package/skills/audit-logging/SKILL.md +0 -197
  166. package/skills/audit-matrix/SKILL.md +0 -245
  167. package/skills/audit-meta/SKILL.md +0 -120
  168. package/skills/audit-naming/SKILL.md +0 -200
  169. package/skills/audit-owasp/SKILL.md +0 -223
  170. package/skills/audit-performance/SKILL.md +0 -199
  171. package/skills/audit-reinvention/SKILL.md +0 -214
  172. package/skills/audit-secrets/SKILL.md +0 -198
  173. package/skills/audit-tests/SKILL.md +0 -210
  174. package/skills/audit-validation/SKILL.md +0 -206
  175. package/skills/audit-verify/SKILL.md +0 -139
  176. package/skills/audit-yagni/SKILL.md +0 -188
  177. package/skills/doc-gen/SKILL.md +0 -490
  178. package/skills/doc-gen/scripts/doc_gen.py +0 -911
  179. package/skills/generate-project-docs/SKILL.md +0 -380
  180. package/skills/implement-project/SKILL.md +0 -409
  181. package/skills/liteend-init/SKILL.md +0 -84
  182. package/skills/litefront-init/SKILL.md +0 -96
  183. package/skills/litefront-prototype/SKILL.md +0 -484
  184. package/skills/ops/SKILL.md +0 -94
  185. package/skills/post-call-task-builder/SKILL.md +0 -419
  186. package/skills/project-setup-standardizer/SKILL.md +0 -285
  187. package/skills/skills-best-practices/SKILL.md +0 -415
  188. package/skills/start/SKILL.md +0 -319
  189. package/skills/tech-blueprint/SKILL.md +0 -890
  190. package/skills/tech-blueprint/scripts/blueprint_validator.py +0 -417
  191. package/src/app.module.ts +0 -29
  192. package/src/commands/clean/clean.command.ts +0 -235
  193. package/src/commands/clean/clean.module.ts +0 -13
  194. package/src/commands/init/init.command.ts +0 -92
  195. package/src/commands/init/init.module.ts +0 -9
  196. package/src/commands/ops/ops-add.command.ts +0 -83
  197. package/src/commands/ops/ops-init.command.ts +0 -125
  198. package/src/commands/ops/ops-list.command.ts +0 -57
  199. package/src/commands/ops/ops-path.command.ts +0 -38
  200. package/src/commands/ops/ops-runbook.command.ts +0 -74
  201. package/src/commands/ops/ops-status.command.ts +0 -47
  202. package/src/commands/ops/ops-use.command.ts +0 -76
  203. package/src/commands/ops/ops.command.ts +0 -42
  204. package/src/commands/ops/ops.helpers.ts +0 -20
  205. package/src/commands/ops/ops.module.ts +0 -23
  206. package/src/commands/pack/pack.command.ts +0 -347
  207. package/src/commands/pack/pack.module.ts +0 -14
  208. package/src/core/config/config.module.ts +0 -10
  209. package/src/core/config/config.schema.ts +0 -58
  210. package/src/core/config/config.service.ts +0 -43
  211. package/src/core/config/prompt.service.ts +0 -80
  212. package/src/core/file-system/fs.module.ts +0 -8
  213. package/src/core/file-system/fs.service.ts +0 -248
  214. package/src/core/registry/registry.module.ts +0 -9
  215. package/src/core/registry/registry.schema.ts +0 -46
  216. package/src/core/registry/registry.service.ts +0 -128
  217. package/src/core/ui/ui.module.ts +0 -9
  218. package/src/core/ui/ui.service.ts +0 -39
  219. package/src/main.ts +0 -12
  220. package/src/shared/cleaner/cleaner.service.ts +0 -289
  221. package/src/shared/cleaner/cleaner.types.ts +0 -23
  222. package/src/shared/constants.ts +0 -118
  223. package/src/shared/deps/deps.module.ts +0 -8
  224. package/src/shared/deps/deps.service.ts +0 -175
  225. package/src/shared/git/git.module.ts +0 -8
  226. package/src/shared/git/git.service.ts +0 -47
  227. package/src/shared/runbook/runbook.module.ts +0 -9
  228. package/src/shared/runbook/runbook.service.ts +0 -164
  229. package/src/shared/runbook/runbook.templates.ts +0 -66
  230. package/src/shared/tokenizer/tokenizer.module.ts +0 -8
  231. package/src/shared/tokenizer/tokenizer.service.ts +0 -30
  232. package/tsconfig.build.json +0 -7
  233. 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
- }