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,13 +0,0 @@
1
- import { Module } from '@nestjs/common';
2
- import { ConfigModule } from '../../core/config/config.module';
3
- import { FsModule } from '../../core/file-system/fs.module';
4
- import { UiModule } from '../../core/ui/ui.module';
5
- import { CleanerService } from '../../shared/cleaner/cleaner.service';
6
- import { GitModule } from '../../shared/git/git.module';
7
- import { CleanCommand } from './clean.command';
8
-
9
- @Module({
10
- imports: [FsModule, UiModule, ConfigModule, GitModule],
11
- providers: [CleanCommand, CleanerService],
12
- })
13
- export class CleanModule {}
@@ -1,92 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
- import path from 'node:path';
3
- import { Command, CommandRunner } from 'nest-commander';
4
- import { UiService } from '../../core/ui/ui.service';
5
-
6
- const GITIGNORE_ENTRY = '.kodu/context.txt';
7
-
8
- const DEFAULT_KODU_JSON = {
9
- $schema:
10
- 'https://raw.githubusercontent.com/anomalyco/kodu/main/kodu.schema.json',
11
- cleaner: {
12
- whitelist: ['//!'],
13
- keepJSDoc: true,
14
- useGitignore: true,
15
- ignore: [],
16
- },
17
- packer: {
18
- ignore: [
19
- 'package-lock.json',
20
- 'yarn.lock',
21
- 'pnpm-lock.yaml',
22
- '.git',
23
- '.kodu',
24
- 'node_modules',
25
- 'dist',
26
- 'coverage',
27
- ],
28
- useGitignore: true,
29
- contentBasedBinaryDetection: false,
30
- },
31
- };
32
-
33
- @Command({ name: 'init', description: 'Initialize kodu configuration' })
34
- export class InitCommand extends CommandRunner {
35
- constructor(private readonly ui: UiService) {
36
- super();
37
- }
38
-
39
- async run(): Promise<void> {
40
- await this.ensureKoduJson();
41
- await this.updateGitignore();
42
- this.ui.log.success('Done.');
43
- }
44
-
45
- private async ensureKoduJson(): Promise<void> {
46
- const configPath = path.join(process.cwd(), 'kodu.json');
47
-
48
- if (await this.exists(configPath)) {
49
- this.ui.log.info('kodu.json already exists');
50
- return;
51
- }
52
-
53
- await fs.writeFile(
54
- configPath,
55
- `${JSON.stringify(DEFAULT_KODU_JSON, null, 2)}\n`,
56
- 'utf8',
57
- );
58
- this.ui.log.success('Created kodu.json');
59
- }
60
-
61
- private async updateGitignore(): Promise<void> {
62
- const gitignorePath = path.join(process.cwd(), '.gitignore');
63
-
64
- if (!(await this.exists(gitignorePath))) {
65
- this.ui.log.warn('.gitignore not found, skipping.');
66
- return;
67
- }
68
-
69
- const content = await fs.readFile(gitignorePath, 'utf8');
70
- const lines = content.split(/\r?\n/);
71
-
72
- if (lines.some((line) => line.trim() === GITIGNORE_ENTRY)) {
73
- this.ui.log.info(`${GITIGNORE_ENTRY} already in .gitignore`);
74
- return;
75
- }
76
-
77
- const trimmed = content.trimEnd();
78
- const next =
79
- trimmed.length > 0 ? `${trimmed}\n${GITIGNORE_ENTRY}` : GITIGNORE_ENTRY;
80
- await fs.writeFile(gitignorePath, `${next}\n`, 'utf8');
81
- this.ui.log.success(`Added ${GITIGNORE_ENTRY} to .gitignore`);
82
- }
83
-
84
- private async exists(target: string): Promise<boolean> {
85
- try {
86
- await fs.access(target);
87
- return true;
88
- } catch {
89
- return false;
90
- }
91
- }
92
- }
@@ -1,9 +0,0 @@
1
- import { Module } from '@nestjs/common';
2
- import { UiModule } from '../../core/ui/ui.module';
3
- import { InitCommand } from './init.command';
4
-
5
- @Module({
6
- imports: [UiModule],
7
- providers: [InitCommand],
8
- })
9
- export class InitModule {}
@@ -1,83 +0,0 @@
1
- import path from 'node:path';
2
- import { CommandRunner, Option, SubCommand } from 'nest-commander';
3
- import { DEFAULT_STANDS } from '../../core/registry/registry.schema';
4
- import { RegistryService } from '../../core/registry/registry.service';
5
- import { UiService } from '../../core/ui/ui.service';
6
-
7
- type AddOptions = {
8
- path?: string;
9
- repo?: string;
10
- // Свойство называется по длинному флагу `--stand` (см. nest-commander/commander).
11
- stand?: string[];
12
- };
13
-
14
- @SubCommand({
15
- name: 'add',
16
- description:
17
- 'Зарегистрировать проект в глобальном реестре по уникальному имени',
18
- arguments: '<name>',
19
- })
20
- export class OpsAddCommand extends CommandRunner {
21
- constructor(
22
- private readonly ui: UiService,
23
- private readonly registry: RegistryService,
24
- ) {
25
- super();
26
- }
27
-
28
- @Option({
29
- flags: '-p, --path <dir>',
30
- description: 'Путь к репозиторию (по умолчанию текущая директория)',
31
- })
32
- parsePath(value: string): string {
33
- return value;
34
- }
35
-
36
- @Option({
37
- flags: '-r, --repo <url>',
38
- description: 'URL репозитория (git clone)',
39
- })
40
- parseRepo(value: string): string {
41
- return value;
42
- }
43
-
44
- @Option({
45
- flags: '-s, --stand <stand>',
46
- description: 'Стенд проекта (можно повторять)',
47
- })
48
- parseStand(value: string, previous: string[] = []): string[] {
49
- return [...previous, value];
50
- }
51
-
52
- async run(inputs: string[], options: AddOptions): Promise<void> {
53
- const name = inputs[0];
54
-
55
- if (!name) {
56
- this.ui.log.error(
57
- 'Укажи имя проекта: kodu ops add <name> [--path <dir>]',
58
- );
59
- process.exitCode = 1;
60
- return;
61
- }
62
-
63
- const projectPath = path.resolve(options.path ?? process.cwd());
64
- const stands =
65
- options.stand && options.stand.length > 0
66
- ? options.stand
67
- : [...DEFAULT_STANDS];
68
-
69
- try {
70
- await this.registry.add(name, {
71
- path: projectPath,
72
- repo: options.repo,
73
- stands,
74
- });
75
- this.ui.log.success(`Проект "${name}" добавлен в реестр.`);
76
- this.ui.log.info(`Путь: ${projectPath}`);
77
- this.ui.log.info(`Стенды: ${stands.join(', ')}`);
78
- } catch (error) {
79
- this.ui.log.error((error as Error).message);
80
- process.exitCode = 1;
81
- }
82
- }
83
- }
@@ -1,125 +0,0 @@
1
- import path from 'node:path';
2
- import { CommandRunner, Option, SubCommand } from 'nest-commander';
3
- import { DEFAULT_STANDS } from '../../core/registry/registry.schema';
4
- import { RegistryService } from '../../core/registry/registry.service';
5
- import { UiService } from '../../core/ui/ui.service';
6
- import { RunbookService } from '../../shared/runbook/runbook.service';
7
-
8
- type InitOptions = {
9
- name?: string;
10
- // Свойства называются по длинным флагам `--active` / `--stand`.
11
- active?: string;
12
- stand?: string[];
13
- };
14
-
15
- @SubCommand({
16
- name: 'init',
17
- description:
18
- 'Настроить стенды в текущем проекте: создать .runbook/, .gitignore и зарегистрировать проект',
19
- })
20
- export class OpsInitCommand extends CommandRunner {
21
- constructor(
22
- private readonly ui: UiService,
23
- private readonly registry: RegistryService,
24
- private readonly runbook: RunbookService,
25
- ) {
26
- super();
27
- }
28
-
29
- @Option({
30
- flags: '-n, --name <name>',
31
- description: 'Уникальное имя проекта (по умолчанию — имя папки)',
32
- })
33
- parseName(value: string): string {
34
- return value;
35
- }
36
-
37
- @Option({
38
- flags: '-a, --active <stand>',
39
- description: 'Активный стенд по умолчанию (по умолчанию local)',
40
- })
41
- parseActive(value: string): string {
42
- return value;
43
- }
44
-
45
- @Option({
46
- flags: '-s, --stand <stand>',
47
- description: 'Стенд проекта (можно повторять)',
48
- })
49
- parseStand(value: string, previous: string[] = []): string[] {
50
- return [...previous, value];
51
- }
52
-
53
- async run(_inputs: string[], options: InitOptions): Promise<void> {
54
- const root = process.cwd();
55
- const name = options.name ?? path.basename(root);
56
- const stands =
57
- options.stand && options.stand.length > 0
58
- ? options.stand
59
- : [...DEFAULT_STANDS];
60
- const activeStand = options.active ?? stands[0] ?? 'local';
61
-
62
- try {
63
- // 1. Скаффолд .runbook/ (config.json + runbook.md).
64
- await this.runbook.scaffold({ project: name, stands, activeStand }, root);
65
- this.ui.log.success(`Создан .runbook/ для проекта "${name}".`);
66
- this.ui.log.info(`Активный стенд: ${activeStand}`);
67
- this.ui.log.info(
68
- `Заполни шаги деплоя в ${this.runbook.runbookPath(root)}`,
69
- );
70
-
71
- // 2. Гарантируем игнор .runbook/ — автоматически.
72
- const gitignore = await this.runbook.ensureGitignore(root);
73
- this.reportGitignore(gitignore);
74
-
75
- // 3. Регистрируем проект в глобальном реестре (имя уникально).
76
- await this.registerProject(name, root, stands);
77
- } catch (error) {
78
- this.ui.log.error((error as Error).message);
79
- process.exitCode = 1;
80
- }
81
- }
82
-
83
- private reportGitignore(result: string): void {
84
- switch (result) {
85
- case 'created':
86
- this.ui.log.success('Создан .gitignore с записью /.runbook/');
87
- break;
88
- case 'added':
89
- this.ui.log.success('Добавил /.runbook/ в .gitignore');
90
- break;
91
- case 'present':
92
- this.ui.log.info('/.runbook/ уже в .gitignore');
93
- break;
94
- case 'no-git':
95
- this.ui.log.warn(
96
- 'Это не git-репозиторий — .gitignore не настроен. Не коммить .runbook/ вручную.',
97
- );
98
- break;
99
- }
100
- }
101
-
102
- private async registerProject(
103
- name: string,
104
- root: string,
105
- stands: string[],
106
- ): Promise<void> {
107
- const existing = await this.registry.get(name);
108
-
109
- if (!existing) {
110
- await this.registry.add(name, { path: root, stands });
111
- this.ui.log.success(`Проект "${name}" добавлен в реестр.`);
112
- return;
113
- }
114
-
115
- if (existing.path === root) {
116
- this.ui.log.info(`Проект "${name}" уже в реестре.`);
117
- return;
118
- }
119
-
120
- this.ui.log.warn(
121
- `Имя "${name}" уже занято другим путём (${existing.path}). ` +
122
- 'Запусти заново с другим именем: kodu ops init --name <другое-имя>',
123
- );
124
- }
125
- }
@@ -1,57 +0,0 @@
1
- import { CommandRunner, SubCommand } from 'nest-commander';
2
- import { RegistryService } from '../../core/registry/registry.service';
3
- import { UiService } from '../../core/ui/ui.service';
4
- import { RunbookService } from '../../shared/runbook/runbook.service';
5
-
6
- @SubCommand({
7
- name: 'list',
8
- aliases: ['ls'],
9
- description: 'Показать все проекты из реестра и их активные стенды',
10
- })
11
- export class OpsListCommand extends CommandRunner {
12
- constructor(
13
- private readonly ui: UiService,
14
- private readonly registry: RegistryService,
15
- private readonly runbook: RunbookService,
16
- ) {
17
- super();
18
- }
19
-
20
- async run(): Promise<void> {
21
- try {
22
- const projects = await this.registry.list();
23
- const names = Object.keys(projects).sort((a, b) => a.localeCompare(b));
24
-
25
- if (names.length === 0) {
26
- this.ui.log.info(
27
- 'Реестр пуст. Добавь проект: kodu ops add <name> --path <dir>',
28
- );
29
- this.ui.log.info(`Файл реестра: ${this.registry.getFilePath()}`);
30
- return;
31
- }
32
-
33
- for (const name of names) {
34
- const entry = projects[name];
35
- const active = await this.readActiveStand(entry.path);
36
- const activeLabel = active ? ` [активный: ${active}]` : '';
37
- this.ui.log.info(`${name}${activeLabel}`);
38
- this.ui.log.info(` путь: ${entry.path}`);
39
- this.ui.log.info(` стенды: ${entry.stands.join(', ')}`);
40
- }
41
- } catch (error) {
42
- this.ui.log.error((error as Error).message);
43
- process.exitCode = 1;
44
- }
45
- }
46
-
47
- private async readActiveStand(root: string): Promise<string | undefined> {
48
- try {
49
- if (!(await this.runbook.exists(root))) {
50
- return undefined;
51
- }
52
- return (await this.runbook.readConfig(root)).activeStand;
53
- } catch {
54
- return undefined;
55
- }
56
- }
57
- }
@@ -1,38 +0,0 @@
1
- import { CommandRunner, SubCommand } from 'nest-commander';
2
- import { RegistryService } from '../../core/registry/registry.service';
3
- import { UiService } from '../../core/ui/ui.service';
4
- import { resolveProjectRoot } from './ops.helpers';
5
-
6
- @SubCommand({
7
- name: 'path',
8
- description:
9
- 'Напечатать путь к репозиторию проекта (удобно для cd $(kodu ops path <name>))',
10
- arguments: '<name>',
11
- })
12
- export class OpsPathCommand extends CommandRunner {
13
- constructor(
14
- private readonly ui: UiService,
15
- private readonly registry: RegistryService,
16
- ) {
17
- super();
18
- }
19
-
20
- async run(inputs: string[]): Promise<void> {
21
- const name = inputs[0];
22
-
23
- if (!name) {
24
- this.ui.log.error('Укажи имя проекта: kodu ops path <name>');
25
- process.exitCode = 1;
26
- return;
27
- }
28
-
29
- try {
30
- const root = await resolveProjectRoot(this.registry, name);
31
- // Чистый вывод пути в stdout — чтобы работало в подстановке команды.
32
- process.stdout.write(`${root}\n`);
33
- } catch (error) {
34
- this.ui.log.error((error as Error).message);
35
- process.exitCode = 1;
36
- }
37
- }
38
- }
@@ -1,74 +0,0 @@
1
- import { CommandRunner, SubCommand } from 'nest-commander';
2
- import { RegistryService } from '../../core/registry/registry.service';
3
- import { UiService } from '../../core/ui/ui.service';
4
- import { RunbookService } from '../../shared/runbook/runbook.service';
5
- import { resolveProjectRoot } from './ops.helpers';
6
-
7
- @SubCommand({
8
- name: 'runbook',
9
- description: 'Напечатать runbook проекта (или секцию конкретного стенда)',
10
- arguments: '<name> [stand]',
11
- })
12
- export class OpsRunbookCommand extends CommandRunner {
13
- constructor(
14
- private readonly ui: UiService,
15
- private readonly registry: RegistryService,
16
- private readonly runbook: RunbookService,
17
- ) {
18
- super();
19
- }
20
-
21
- async run(inputs: string[]): Promise<void> {
22
- const name = inputs[0];
23
- const stand = inputs[1];
24
-
25
- if (!name) {
26
- this.ui.log.error('Укажи имя проекта: kodu ops runbook <name> [stand]');
27
- process.exitCode = 1;
28
- return;
29
- }
30
-
31
- try {
32
- const root = await resolveProjectRoot(this.registry, name);
33
-
34
- if (!(await this.runbook.exists(root))) {
35
- this.ui.log.warn(
36
- `В проекте "${name}" нет .runbook/. Запусти: kodu ops init`,
37
- );
38
- process.exitCode = 1;
39
- return;
40
- }
41
-
42
- const markdown = await this.runbook.readRunbook(root);
43
- const output = stand ? this.extractStand(markdown, stand) : markdown;
44
-
45
- if (!output) {
46
- this.ui.log.warn(`Секция для стенда "${stand}" не найдена в runbook.`);
47
- process.exitCode = 1;
48
- return;
49
- }
50
-
51
- process.stdout.write(`${output.trimEnd()}\n`);
52
- } catch (error) {
53
- this.ui.log.error((error as Error).message);
54
- process.exitCode = 1;
55
- }
56
- }
57
-
58
- /** Возвращает блок `## Стенд: <stand> ...` до следующего заголовка `## `. */
59
- private extractStand(markdown: string, stand: string): string | undefined {
60
- const lines = markdown.split(/\r?\n/);
61
- const startPrefix = `## Стенд: ${stand}`;
62
- const start = lines.findIndex((line) => line.startsWith(startPrefix));
63
-
64
- if (start === -1) {
65
- return undefined;
66
- }
67
-
68
- const rest = lines.slice(start + 1);
69
- const nextHeading = rest.findIndex((line) => line.startsWith('## '));
70
- const end = nextHeading === -1 ? lines.length : start + 1 + nextHeading;
71
-
72
- return lines.slice(start, end).join('\n');
73
- }
74
- }
@@ -1,47 +0,0 @@
1
- import { CommandRunner, SubCommand } from 'nest-commander';
2
- import { RegistryService } from '../../core/registry/registry.service';
3
- import { UiService } from '../../core/ui/ui.service';
4
- import { RunbookService } from '../../shared/runbook/runbook.service';
5
- import { resolveProjectRoot } from './ops.helpers';
6
-
7
- @SubCommand({
8
- name: 'status',
9
- description:
10
- 'Показать активный стенд и стенды проекта (по имени или в текущей папке)',
11
- arguments: '[name]',
12
- })
13
- export class OpsStatusCommand extends CommandRunner {
14
- constructor(
15
- private readonly ui: UiService,
16
- private readonly registry: RegistryService,
17
- private readonly runbook: RunbookService,
18
- ) {
19
- super();
20
- }
21
-
22
- async run(inputs: string[]): Promise<void> {
23
- try {
24
- const name = inputs[0];
25
- const root = name
26
- ? await resolveProjectRoot(this.registry, name)
27
- : process.cwd();
28
-
29
- if (!(await this.runbook.exists(root))) {
30
- this.ui.log.warn(
31
- `В ${root} нет .runbook/. Инициализируй проект: kodu ops init`,
32
- );
33
- process.exitCode = 1;
34
- return;
35
- }
36
-
37
- const config = await this.runbook.readConfig(root);
38
- this.ui.log.info(`Проект: ${config.project}`);
39
- this.ui.log.info(`Активный стенд: ${config.activeStand}`);
40
- this.ui.log.info(`Стенды: ${config.stands.join(', ')}`);
41
- this.ui.log.info(`Путь: ${root}`);
42
- } catch (error) {
43
- this.ui.log.error((error as Error).message);
44
- process.exitCode = 1;
45
- }
46
- }
47
- }
@@ -1,76 +0,0 @@
1
- import { CommandRunner, SubCommand } from 'nest-commander';
2
- import { RegistryService } from '../../core/registry/registry.service';
3
- import { UiService } from '../../core/ui/ui.service';
4
- import { RunbookService } from '../../shared/runbook/runbook.service';
5
- import { resolveProjectRoot } from './ops.helpers';
6
-
7
- @SubCommand({
8
- name: 'use',
9
- aliases: ['switch'],
10
- description:
11
- 'Переключить активный стенд: "kodu ops use <stand>" в текущем проекте или "kodu ops use <name> <stand>"',
12
- arguments: '<args...>',
13
- })
14
- export class OpsUseCommand extends CommandRunner {
15
- constructor(
16
- private readonly ui: UiService,
17
- private readonly registry: RegistryService,
18
- private readonly runbook: RunbookService,
19
- ) {
20
- super();
21
- }
22
-
23
- async run(inputs: string[]): Promise<void> {
24
- try {
25
- const { root, stand } = await this.resolveTarget(inputs);
26
-
27
- if (!stand) {
28
- this.ui.log.error(
29
- 'Укажи стенд: kodu ops use <stand> или kodu ops use <name> <stand>',
30
- );
31
- process.exitCode = 1;
32
- return;
33
- }
34
-
35
- if (!(await this.runbook.exists(root))) {
36
- this.ui.log.warn(
37
- `В ${root} нет .runbook/. Инициализируй проект: kodu ops init`,
38
- );
39
- process.exitCode = 1;
40
- return;
41
- }
42
-
43
- const config = await this.runbook.readConfig(root);
44
- const stands = config.stands.includes(stand)
45
- ? config.stands
46
- : [...config.stands, stand];
47
-
48
- if (!config.stands.includes(stand)) {
49
- this.ui.log.info(`Стенд "${stand}" добавлен в список стендов проекта.`);
50
- }
51
-
52
- await this.runbook.writeConfig(
53
- { ...config, activeStand: stand, stands },
54
- root,
55
- );
56
- this.ui.log.success(
57
- `Активный стенд проекта "${config.project}" → ${stand}`,
58
- );
59
- } catch (error) {
60
- this.ui.log.error((error as Error).message);
61
- process.exitCode = 1;
62
- }
63
- }
64
-
65
- /** 1 аргумент → стенд в текущей папке; 2 аргумента → <name> <stand>. */
66
- private async resolveTarget(
67
- inputs: string[],
68
- ): Promise<{ root: string; stand?: string }> {
69
- if (inputs.length >= 2) {
70
- const root = await resolveProjectRoot(this.registry, inputs[0]);
71
- return { root, stand: inputs[1] };
72
- }
73
-
74
- return { root: process.cwd(), stand: inputs[0] };
75
- }
76
- }
@@ -1,42 +0,0 @@
1
- import { Command, CommandRunner } from 'nest-commander';
2
- import { UiService } from '../../core/ui/ui.service';
3
- import { OpsAddCommand } from './ops-add.command';
4
- import { OpsInitCommand } from './ops-init.command';
5
- import { OpsListCommand } from './ops-list.command';
6
- import { OpsPathCommand } from './ops-path.command';
7
- import { OpsRunbookCommand } from './ops-runbook.command';
8
- import { OpsStatusCommand } from './ops-status.command';
9
- import { OpsUseCommand } from './ops-use.command';
10
-
11
- @Command({
12
- name: 'ops',
13
- description:
14
- 'Работа с проектами и стендами (local/dev/stage/prod) из любого места',
15
- subCommands: [
16
- OpsInitCommand,
17
- OpsListCommand,
18
- OpsAddCommand,
19
- OpsStatusCommand,
20
- OpsUseCommand,
21
- OpsPathCommand,
22
- OpsRunbookCommand,
23
- ],
24
- })
25
- export class OpsCommand extends CommandRunner {
26
- constructor(private readonly ui: UiService) {
27
- super();
28
- }
29
-
30
- async run(): Promise<void> {
31
- this.ui.log.info('Использование: kodu ops <команда>');
32
- this.ui.log.info(
33
- ' init — настроить стенды в текущем проекте',
34
- );
35
- this.ui.log.info(' list — список всех проектов');
36
- this.ui.log.info(' add <name> --path <d> — зарегистрировать проект');
37
- this.ui.log.info(' status [name] — активный стенд проекта');
38
- this.ui.log.info(' use <stand> — переключить активный стенд');
39
- this.ui.log.info(' path <name> — путь к репозиторию проекта');
40
- this.ui.log.info(' runbook <name> [stand]— показать инструкции по стенду');
41
- }
42
- }
@@ -1,20 +0,0 @@
1
- import { RegistryService } from '../../core/registry/registry.service';
2
-
3
- /**
4
- * Возвращает путь к репозиторию проекта по его имени из реестра.
5
- * Бросает понятную ошибку, если проект не найден.
6
- */
7
- export async function resolveProjectRoot(
8
- registry: RegistryService,
9
- name: string,
10
- ): Promise<string> {
11
- const entry = await registry.get(name);
12
-
13
- if (!entry) {
14
- throw new Error(
15
- `Проект "${name}" не найден в реестре. Список проектов: kodu ops list`,
16
- );
17
- }
18
-
19
- return entry.path;
20
- }