@corbat-tech/coding-standards-mcp 1.0.0

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 (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +371 -0
  3. package/assets/demo.gif +0 -0
  4. package/dist/agent.d.ts +53 -0
  5. package/dist/agent.d.ts.map +1 -0
  6. package/dist/agent.js +629 -0
  7. package/dist/agent.js.map +1 -0
  8. package/dist/cli/init.d.ts +3 -0
  9. package/dist/cli/init.d.ts.map +1 -0
  10. package/dist/cli/init.js +651 -0
  11. package/dist/cli/init.js.map +1 -0
  12. package/dist/config.d.ts +73 -0
  13. package/dist/config.d.ts.map +1 -0
  14. package/dist/config.js +105 -0
  15. package/dist/config.js.map +1 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +73 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/profiles.d.ts +39 -0
  21. package/dist/profiles.d.ts.map +1 -0
  22. package/dist/profiles.js +526 -0
  23. package/dist/profiles.js.map +1 -0
  24. package/dist/prompts-legacy.d.ts +25 -0
  25. package/dist/prompts-legacy.d.ts.map +1 -0
  26. package/dist/prompts-legacy.js +600 -0
  27. package/dist/prompts-legacy.js.map +1 -0
  28. package/dist/prompts-v2.d.ts +30 -0
  29. package/dist/prompts-v2.d.ts.map +1 -0
  30. package/dist/prompts-v2.js +310 -0
  31. package/dist/prompts-v2.js.map +1 -0
  32. package/dist/prompts.d.ts +30 -0
  33. package/dist/prompts.d.ts.map +1 -0
  34. package/dist/prompts.js +310 -0
  35. package/dist/prompts.js.map +1 -0
  36. package/dist/resources.d.ts +18 -0
  37. package/dist/resources.d.ts.map +1 -0
  38. package/dist/resources.js +95 -0
  39. package/dist/resources.js.map +1 -0
  40. package/dist/tools-legacy.d.ts +196 -0
  41. package/dist/tools-legacy.d.ts.map +1 -0
  42. package/dist/tools-legacy.js +1230 -0
  43. package/dist/tools-legacy.js.map +1 -0
  44. package/dist/tools-v2.d.ts +92 -0
  45. package/dist/tools-v2.d.ts.map +1 -0
  46. package/dist/tools-v2.js +410 -0
  47. package/dist/tools-v2.js.map +1 -0
  48. package/dist/tools.d.ts +92 -0
  49. package/dist/tools.d.ts.map +1 -0
  50. package/dist/tools.js +410 -0
  51. package/dist/tools.js.map +1 -0
  52. package/dist/types.d.ts +3054 -0
  53. package/dist/types.d.ts.map +1 -0
  54. package/dist/types.js +515 -0
  55. package/dist/types.js.map +1 -0
  56. package/dist/utils/index.d.ts +6 -0
  57. package/dist/utils/index.d.ts.map +1 -0
  58. package/dist/utils/index.js +5 -0
  59. package/dist/utils/index.js.map +1 -0
  60. package/dist/utils/retry.d.ts +44 -0
  61. package/dist/utils/retry.d.ts.map +1 -0
  62. package/dist/utils/retry.js +74 -0
  63. package/dist/utils/retry.js.map +1 -0
  64. package/package.json +79 -0
  65. package/profiles/README.md +199 -0
  66. package/profiles/custom/.gitkeep +2 -0
  67. package/profiles/templates/_template.yaml +159 -0
  68. package/profiles/templates/angular.yaml +494 -0
  69. package/profiles/templates/java-spring-backend.yaml +512 -0
  70. package/profiles/templates/minimal.yaml +102 -0
  71. package/profiles/templates/nodejs.yaml +338 -0
  72. package/profiles/templates/python.yaml +340 -0
  73. package/profiles/templates/react.yaml +331 -0
  74. package/profiles/templates/vue.yaml +598 -0
  75. package/standards/architecture/ddd.md +173 -0
  76. package/standards/architecture/hexagonal.md +97 -0
  77. package/standards/cicd/github-actions.md +567 -0
  78. package/standards/clean-code/naming.md +175 -0
  79. package/standards/clean-code/principles.md +179 -0
  80. package/standards/containerization/dockerfile.md +419 -0
  81. package/standards/database/selection-guide.md +443 -0
  82. package/standards/documentation/guidelines.md +189 -0
  83. package/standards/event-driven/domain-events.md +527 -0
  84. package/standards/kubernetes/deployment.md +518 -0
  85. package/standards/observability/guidelines.md +665 -0
  86. package/standards/project-setup/initialization-checklist.md +650 -0
  87. package/standards/spring-boot/best-practices.md +598 -0
  88. package/standards/testing/guidelines.md +559 -0
  89. package/standards/workflow/llm-development-workflow.md +542 -0
@@ -0,0 +1,651 @@
1
+ #!/usr/bin/env node
2
+ import { access, readFile, readdir, writeFile } from 'node:fs/promises';
3
+ import { dirname, join } from 'node:path';
4
+ import { createInterface } from 'node:readline';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { stringify } from 'yaml';
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+ // Colors for terminal output
10
+ const colors = {
11
+ reset: '\x1b[0m',
12
+ bright: '\x1b[1m',
13
+ dim: '\x1b[2m',
14
+ green: '\x1b[32m',
15
+ yellow: '\x1b[33m',
16
+ blue: '\x1b[34m',
17
+ magenta: '\x1b[35m',
18
+ cyan: '\x1b[36m',
19
+ red: '\x1b[31m',
20
+ };
21
+ const c = colors;
22
+ class CorbatInit {
23
+ rl;
24
+ projectDir;
25
+ detectedInfo = { language: 'unknown' };
26
+ constructor() {
27
+ this.rl = createInterface({
28
+ input: process.stdin,
29
+ output: process.stdout,
30
+ });
31
+ this.projectDir = process.cwd();
32
+ }
33
+ print(text) {
34
+ console.log(text);
35
+ }
36
+ async question(prompt) {
37
+ return new Promise((resolve) => {
38
+ this.rl.question(prompt, (answer) => {
39
+ resolve(answer.trim());
40
+ });
41
+ });
42
+ }
43
+ async select(prompt, options, defaultIndex = 0) {
44
+ this.print(`\n${c.cyan}${prompt}${c.reset}`);
45
+ options.forEach((opt, i) => {
46
+ const marker = i === defaultIndex ? `${c.green}→${c.reset}` : ' ';
47
+ const highlight = i === defaultIndex ? c.bright : c.dim;
48
+ this.print(` ${marker} ${highlight}${i + 1}) ${opt}${c.reset}`);
49
+ });
50
+ const answer = await this.question(`\n${c.yellow}Enter number [${defaultIndex + 1}]: ${c.reset}`);
51
+ const index = answer ? Number.parseInt(answer, 10) - 1 : defaultIndex;
52
+ if (index >= 0 && index < options.length) {
53
+ return options[index];
54
+ }
55
+ return options[defaultIndex];
56
+ }
57
+ async confirm(prompt, defaultYes = true) {
58
+ const hint = defaultYes ? '[Y/n]' : '[y/N]';
59
+ const answer = await this.question(`${c.cyan}${prompt}${c.reset} ${c.dim}${hint}${c.reset} `);
60
+ if (!answer)
61
+ return defaultYes;
62
+ return answer.toLowerCase().startsWith('y');
63
+ }
64
+ async input(prompt, defaultValue) {
65
+ const hint = defaultValue ? ` ${c.dim}[${defaultValue}]${c.reset}` : '';
66
+ const answer = await this.question(`${c.cyan}${prompt}${c.reset}${hint}: `);
67
+ return answer || defaultValue || '';
68
+ }
69
+ async detectProject() {
70
+ this.print(`\n${c.blue}🔍 Scanning project...${c.reset}\n`);
71
+ // Check for Java/Maven/Gradle
72
+ try {
73
+ await access(join(this.projectDir, 'pom.xml'));
74
+ this.detectedInfo.language = 'Java';
75
+ this.detectedInfo.buildTool = 'Maven';
76
+ const pomContent = await readFile(join(this.projectDir, 'pom.xml'), 'utf-8');
77
+ // Detect Java version
78
+ const javaMatch = pomContent.match(/<java\.version>(\d+)<\/java\.version>/);
79
+ if (javaMatch)
80
+ this.detectedInfo.javaVersion = javaMatch[1];
81
+ // Detect Spring Boot version
82
+ const springMatch = pomContent.match(/<version>(\d+\.\d+\.\d+)<\/version>[\s\S]*?spring-boot/i);
83
+ const springParentMatch = pomContent.match(/spring-boot-starter-parent[\s\S]*?<version>(\d+\.\d+\.\d+)/);
84
+ if (springParentMatch) {
85
+ this.detectedInfo.framework = 'Spring Boot';
86
+ this.detectedInfo.springVersion = springParentMatch[1];
87
+ }
88
+ else if (springMatch) {
89
+ this.detectedInfo.framework = 'Spring Boot';
90
+ this.detectedInfo.springVersion = springMatch[1];
91
+ }
92
+ this.detectedInfo.testFramework = 'JUnit5';
93
+ }
94
+ catch {
95
+ // Not Maven
96
+ }
97
+ try {
98
+ await access(join(this.projectDir, 'build.gradle'));
99
+ this.detectedInfo.language = 'Java';
100
+ this.detectedInfo.buildTool = 'Gradle';
101
+ const gradleContent = await readFile(join(this.projectDir, 'build.gradle'), 'utf-8');
102
+ const javaMatch = gradleContent.match(/sourceCompatibility\s*=\s*['"]?(\d+)/);
103
+ if (javaMatch)
104
+ this.detectedInfo.javaVersion = javaMatch[1];
105
+ if (gradleContent.includes('spring-boot')) {
106
+ this.detectedInfo.framework = 'Spring Boot';
107
+ const springMatch = gradleContent.match(/springBootVersion\s*=\s*['"](\d+\.\d+\.\d+)/);
108
+ if (springMatch)
109
+ this.detectedInfo.springVersion = springMatch[1];
110
+ }
111
+ this.detectedInfo.testFramework = 'JUnit5';
112
+ }
113
+ catch {
114
+ // Not Gradle
115
+ }
116
+ // Check for Node.js/TypeScript
117
+ try {
118
+ await access(join(this.projectDir, 'package.json'));
119
+ const pkgContent = await readFile(join(this.projectDir, 'package.json'), 'utf-8');
120
+ const pkg = JSON.parse(pkgContent);
121
+ if (this.detectedInfo.language === 'unknown') {
122
+ this.detectedInfo.language = pkg.devDependencies?.typescript ? 'TypeScript' : 'JavaScript';
123
+ this.detectedInfo.buildTool = 'npm';
124
+ }
125
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
126
+ if (deps.react)
127
+ this.detectedInfo.framework = 'React';
128
+ else if (deps.vue)
129
+ this.detectedInfo.framework = 'Vue';
130
+ else if (deps['@nestjs/core'])
131
+ this.detectedInfo.framework = 'NestJS';
132
+ else if (deps.express)
133
+ this.detectedInfo.framework = 'Express';
134
+ else if (deps.fastify)
135
+ this.detectedInfo.framework = 'Fastify';
136
+ if (deps.vitest)
137
+ this.detectedInfo.testFramework = 'Vitest';
138
+ else if (deps.jest)
139
+ this.detectedInfo.testFramework = 'Jest';
140
+ else if (deps.mocha)
141
+ this.detectedInfo.testFramework = 'Mocha';
142
+ if (pkg.engines?.node) {
143
+ this.detectedInfo.nodeVersion = pkg.engines.node.replace(/[^\d.]/g, '');
144
+ }
145
+ }
146
+ catch {
147
+ // Not Node.js
148
+ }
149
+ // Check for Python
150
+ try {
151
+ await access(join(this.projectDir, 'pyproject.toml'));
152
+ this.detectedInfo.language = 'Python';
153
+ this.detectedInfo.buildTool = 'Poetry';
154
+ const pyContent = await readFile(join(this.projectDir, 'pyproject.toml'), 'utf-8');
155
+ if (pyContent.includes('fastapi'))
156
+ this.detectedInfo.framework = 'FastAPI';
157
+ else if (pyContent.includes('django'))
158
+ this.detectedInfo.framework = 'Django';
159
+ else if (pyContent.includes('flask'))
160
+ this.detectedInfo.framework = 'Flask';
161
+ const pythonMatch = pyContent.match(/python\s*=\s*["'][\^~]?(\d+\.\d+)/);
162
+ if (pythonMatch)
163
+ this.detectedInfo.pythonVersion = pythonMatch[1];
164
+ this.detectedInfo.testFramework = 'pytest';
165
+ }
166
+ catch {
167
+ // Not Python with pyproject.toml
168
+ }
169
+ try {
170
+ await access(join(this.projectDir, 'requirements.txt'));
171
+ if (this.detectedInfo.language === 'unknown') {
172
+ this.detectedInfo.language = 'Python';
173
+ this.detectedInfo.buildTool = 'pip';
174
+ this.detectedInfo.testFramework = 'pytest';
175
+ }
176
+ }
177
+ catch {
178
+ // No requirements.txt
179
+ }
180
+ // Check for Docker/K8s
181
+ try {
182
+ await access(join(this.projectDir, 'Dockerfile'));
183
+ this.detectedInfo.hasDocker = true;
184
+ }
185
+ catch {
186
+ this.detectedInfo.hasDocker = false;
187
+ }
188
+ try {
189
+ const files = await readdir(this.projectDir);
190
+ this.detectedInfo.hasKubernetes = files.some((f) => f.includes('k8s') || f.includes('kubernetes') || f.endsWith('.yaml'));
191
+ }
192
+ catch {
193
+ this.detectedInfo.hasKubernetes = false;
194
+ }
195
+ // Check for CI/CD
196
+ try {
197
+ await access(join(this.projectDir, '.github/workflows'));
198
+ this.detectedInfo.hasCICD = true;
199
+ }
200
+ catch {
201
+ try {
202
+ await access(join(this.projectDir, '.gitlab-ci.yml'));
203
+ this.detectedInfo.hasCICD = true;
204
+ }
205
+ catch {
206
+ this.detectedInfo.hasCICD = false;
207
+ }
208
+ }
209
+ }
210
+ printDetectedInfo() {
211
+ const info = this.detectedInfo;
212
+ this.print(`${c.green}✓ Detected configuration:${c.reset}\n`);
213
+ this.print(` ${c.bright}Language:${c.reset} ${info.language}`);
214
+ if (info.framework) {
215
+ this.print(` ${c.bright}Framework:${c.reset} ${info.framework}${info.springVersion ? ` ${info.springVersion}` : ''}`);
216
+ }
217
+ if (info.buildTool) {
218
+ this.print(` ${c.bright}Build Tool:${c.reset} ${info.buildTool}`);
219
+ }
220
+ if (info.javaVersion) {
221
+ this.print(` ${c.bright}Java Version:${c.reset} ${info.javaVersion}`);
222
+ }
223
+ if (info.nodeVersion) {
224
+ this.print(` ${c.bright}Node Version:${c.reset} ${info.nodeVersion}`);
225
+ }
226
+ if (info.pythonVersion) {
227
+ this.print(` ${c.bright}Python:${c.reset} ${info.pythonVersion}`);
228
+ }
229
+ if (info.testFramework) {
230
+ this.print(` ${c.bright}Tests:${c.reset} ${info.testFramework}`);
231
+ }
232
+ if (info.hasDocker) {
233
+ this.print(` ${c.bright}Docker:${c.reset} Yes`);
234
+ }
235
+ if (info.hasCICD) {
236
+ this.print(` ${c.bright}CI/CD:${c.reset} Yes`);
237
+ }
238
+ this.print('');
239
+ }
240
+ async runWizard() {
241
+ const info = this.detectedInfo;
242
+ // Profile name
243
+ const defaultName = info.framework
244
+ ? `${info.framework.toLowerCase().replace(/\s+/g, '-')}-project`
245
+ : `${info.language.toLowerCase()}-project`;
246
+ const name = await this.input('Profile name', defaultName);
247
+ const description = await this.input('Description', `${info.framework || info.language} project standards`);
248
+ // Architecture
249
+ this.print(`\n${c.magenta}━━━ Architecture ━━━${c.reset}`);
250
+ const archType = await this.select('Architecture pattern:', [
251
+ 'hexagonal (Ports & Adapters)',
252
+ 'clean (Clean Architecture)',
253
+ 'layered (Traditional N-Tier)',
254
+ 'modular-monolith',
255
+ 'microservices',
256
+ ], 0);
257
+ const enforceLayerDeps = await this.confirm('Enforce layer dependencies?', true);
258
+ // DDD
259
+ this.print(`\n${c.magenta}━━━ Domain-Driven Design ━━━${c.reset}`);
260
+ const dddEnabled = await this.confirm('Enable DDD patterns?', true);
261
+ const ubiquitousLanguage = dddEnabled ? await this.confirm('Enforce ubiquitous language?', true) : false;
262
+ // CQRS
263
+ this.print(`\n${c.magenta}━━━ CQRS ━━━${c.reset}`);
264
+ const cqrsEnabled = await this.confirm('Enable CQRS?', info.framework === 'Spring Boot');
265
+ const cqrsSeparation = cqrsEnabled ? await this.select('CQRS separation:', ['logical', 'physical'], 0) : 'logical';
266
+ // Event-Driven
267
+ this.print(`\n${c.magenta}━━━ Event-Driven Architecture ━━━${c.reset}`);
268
+ const eventDrivenEnabled = await this.confirm('Enable event-driven patterns?', info.framework === 'Spring Boot');
269
+ const eventApproach = eventDrivenEnabled
270
+ ? await this.select('Event approach:', ['domain-events', 'event-sourcing'], 0)
271
+ : 'domain-events';
272
+ // Code Quality
273
+ this.print(`\n${c.magenta}━━━ Code Quality Thresholds ━━━${c.reset}`);
274
+ const maxMethodLinesStr = await this.input('Max lines per method', '20');
275
+ const maxClassLinesStr = await this.input('Max lines per class', '200');
276
+ const maxFileLinesStr = await this.input('Max lines per file', '400');
277
+ const maxParamsStr = await this.input('Max method parameters', '4');
278
+ const minCoverageStr = await this.input('Minimum test coverage %', '80');
279
+ // Testing
280
+ this.print(`\n${c.magenta}━━━ Testing ━━━${c.reset}`);
281
+ let testFramework = info.testFramework || 'JUnit5';
282
+ let assertionLib = 'AssertJ';
283
+ let mockingLib = 'Mockito';
284
+ if (info.language === 'Java') {
285
+ testFramework = await this.select('Test framework:', ['JUnit5', 'JUnit4', 'TestNG'], 0);
286
+ assertionLib = await this.select('Assertion library:', ['AssertJ', 'Hamcrest', 'JUnit assertions'], 0);
287
+ mockingLib = await this.select('Mocking library:', ['Mockito', 'EasyMock', 'JMockit'], 0);
288
+ }
289
+ else if (info.language === 'TypeScript' || info.language === 'JavaScript') {
290
+ testFramework = await this.select('Test framework:', ['Vitest', 'Jest', 'Mocha'], info.testFramework === 'Vitest' ? 0 : 1);
291
+ assertionLib = testFramework === 'Vitest' ? 'Vitest' : 'Jest';
292
+ mockingLib = testFramework === 'Vitest' ? 'Vitest' : 'Jest';
293
+ }
294
+ else if (info.language === 'Python') {
295
+ testFramework = await this.select('Test framework:', ['pytest', 'unittest'], 0);
296
+ assertionLib = 'pytest';
297
+ mockingLib = 'pytest-mock';
298
+ }
299
+ // Technologies
300
+ const technologies = [];
301
+ if (info.language === 'Java' && info.javaVersion) {
302
+ technologies.push({ name: 'Java', version: info.javaVersion });
303
+ }
304
+ if (info.framework) {
305
+ technologies.push({
306
+ name: info.framework,
307
+ version: info.springVersion || undefined,
308
+ });
309
+ }
310
+ if (info.buildTool) {
311
+ technologies.push({ name: info.buildTool });
312
+ }
313
+ return {
314
+ name,
315
+ description,
316
+ architecture: {
317
+ type: archType.split(' ')[0],
318
+ enforceLayerDependencies: enforceLayerDeps,
319
+ },
320
+ ddd: {
321
+ enabled: dddEnabled,
322
+ ubiquitousLanguageEnforced: ubiquitousLanguage,
323
+ },
324
+ cqrs: {
325
+ enabled: cqrsEnabled,
326
+ separation: cqrsSeparation,
327
+ },
328
+ eventDriven: {
329
+ enabled: eventDrivenEnabled,
330
+ approach: eventApproach,
331
+ },
332
+ codeQuality: {
333
+ maxMethodLines: Number.parseInt(maxMethodLinesStr, 10) || 20,
334
+ maxClassLines: Number.parseInt(maxClassLinesStr, 10) || 200,
335
+ maxFileLines: Number.parseInt(maxFileLinesStr, 10) || 400,
336
+ maxMethodParameters: Number.parseInt(maxParamsStr, 10) || 4,
337
+ minimumTestCoverage: Number.parseInt(minCoverageStr, 10) || 80,
338
+ },
339
+ testing: {
340
+ framework: testFramework,
341
+ assertionLibrary: assertionLib,
342
+ mockingLibrary: mockingLib,
343
+ },
344
+ technologies,
345
+ };
346
+ }
347
+ buildYamlProfile(config) {
348
+ const profile = {
349
+ name: config.name,
350
+ description: config.description,
351
+ architecture: {
352
+ type: config.architecture.type,
353
+ enforceLayerDependencies: config.architecture.enforceLayerDependencies,
354
+ layers: this.getDefaultLayers(config.architecture.type),
355
+ },
356
+ ddd: config.ddd.enabled
357
+ ? {
358
+ enabled: true,
359
+ ubiquitousLanguageEnforced: config.ddd.ubiquitousLanguageEnforced,
360
+ patterns: {
361
+ aggregates: true,
362
+ entities: true,
363
+ valueObjects: true,
364
+ domainEvents: config.eventDriven.enabled,
365
+ repositories: true,
366
+ domainServices: true,
367
+ },
368
+ }
369
+ : { enabled: false },
370
+ cqrs: config.cqrs.enabled
371
+ ? {
372
+ enabled: true,
373
+ separation: config.cqrs.separation,
374
+ patterns: {
375
+ commands: { suffix: 'Command', handler: 'CommandHandler' },
376
+ queries: { suffix: 'Query', handler: 'QueryHandler' },
377
+ },
378
+ }
379
+ : { enabled: false },
380
+ eventDriven: config.eventDriven.enabled
381
+ ? {
382
+ enabled: true,
383
+ approach: config.eventDriven.approach,
384
+ patterns: {
385
+ domainEvents: { suffix: 'Event', pastTense: true },
386
+ },
387
+ }
388
+ : { enabled: false },
389
+ codeQuality: {
390
+ maxMethodLines: config.codeQuality.maxMethodLines,
391
+ maxClassLines: config.codeQuality.maxClassLines,
392
+ maxFileLines: config.codeQuality.maxFileLines,
393
+ maxMethodParameters: config.codeQuality.maxMethodParameters,
394
+ maxCyclomaticComplexity: 10,
395
+ requireDocumentation: true,
396
+ requireTests: true,
397
+ minimumTestCoverage: config.codeQuality.minimumTestCoverage,
398
+ principles: ['SOLID', 'DRY', 'KISS', 'YAGNI'],
399
+ },
400
+ naming: this.getNamingConventions(this.detectedInfo.language),
401
+ testing: {
402
+ framework: config.testing.framework,
403
+ assertionLibrary: config.testing.assertionLibrary,
404
+ mockingLibrary: config.testing.mockingLibrary,
405
+ types: {
406
+ unit: { suffix: 'Test', coverage: config.codeQuality.minimumTestCoverage },
407
+ integration: { suffix: 'IT' },
408
+ architecture: { tool: 'ArchUnit', recommended: true },
409
+ },
410
+ },
411
+ technologies: config.technologies,
412
+ };
413
+ return stringify(profile, { lineWidth: 120 });
414
+ }
415
+ getDefaultLayers(archType) {
416
+ if (archType === 'hexagonal') {
417
+ return [
418
+ { name: 'domain', description: 'Core business logic. NO external dependencies.', allowedDependencies: [] },
419
+ {
420
+ name: 'application',
421
+ description: 'Use cases and ports. Depends only on domain.',
422
+ allowedDependencies: ['domain'],
423
+ },
424
+ {
425
+ name: 'infrastructure',
426
+ description: 'Adapters and frameworks.',
427
+ allowedDependencies: ['domain', 'application'],
428
+ },
429
+ ];
430
+ }
431
+ if (archType === 'clean') {
432
+ return [
433
+ { name: 'entities', description: 'Enterprise business rules.', allowedDependencies: [] },
434
+ { name: 'usecases', description: 'Application business rules.', allowedDependencies: ['entities'] },
435
+ { name: 'adapters', description: 'Interface adapters.', allowedDependencies: ['entities', 'usecases'] },
436
+ {
437
+ name: 'frameworks',
438
+ description: 'Frameworks and drivers.',
439
+ allowedDependencies: ['entities', 'usecases', 'adapters'],
440
+ },
441
+ ];
442
+ }
443
+ if (archType === 'layered') {
444
+ return [
445
+ { name: 'presentation', description: 'UI layer.', allowedDependencies: ['business'] },
446
+ { name: 'business', description: 'Business logic.', allowedDependencies: ['data'] },
447
+ { name: 'data', description: 'Data access.', allowedDependencies: [] },
448
+ ];
449
+ }
450
+ return [];
451
+ }
452
+ getNamingConventions(language) {
453
+ if (language === 'Java') {
454
+ return {
455
+ general: {
456
+ class: 'PascalCase',
457
+ interface: 'PascalCase',
458
+ method: 'camelCase',
459
+ variable: 'camelCase',
460
+ constant: 'SCREAMING_SNAKE_CASE',
461
+ package: 'lowercase.dot.separated',
462
+ },
463
+ suffixes: {
464
+ repository: 'Repository',
465
+ service: 'Service',
466
+ controller: 'Controller',
467
+ entity: 'Entity (or none)',
468
+ },
469
+ testing: {
470
+ unitTest: '*Test',
471
+ integrationTest: '*IT',
472
+ testMethod: 'should_ExpectedBehavior_When_Condition',
473
+ },
474
+ };
475
+ }
476
+ if (language === 'TypeScript' || language === 'JavaScript') {
477
+ return {
478
+ general: {
479
+ class: 'PascalCase',
480
+ interface: 'PascalCase (no I prefix)',
481
+ function: 'camelCase',
482
+ variable: 'camelCase',
483
+ constant: 'SCREAMING_SNAKE_CASE',
484
+ file: 'kebab-case',
485
+ },
486
+ suffixes: {
487
+ repository: '.repository',
488
+ service: '.service',
489
+ controller: '.controller',
490
+ types: '.types',
491
+ },
492
+ testing: {
493
+ testFile: '*.test.ts or *.spec.ts',
494
+ testMethod: 'should ... when ...',
495
+ },
496
+ };
497
+ }
498
+ if (language === 'Python') {
499
+ return {
500
+ general: {
501
+ class: 'PascalCase',
502
+ function: 'snake_case',
503
+ variable: 'snake_case',
504
+ constant: 'SCREAMING_SNAKE_CASE',
505
+ module: 'snake_case',
506
+ },
507
+ suffixes: {
508
+ repository: '_repository',
509
+ service: '_service',
510
+ controller: '_controller',
511
+ },
512
+ testing: {
513
+ testFile: 'test_*.py',
514
+ testMethod: 'test_should_*',
515
+ },
516
+ };
517
+ }
518
+ return {
519
+ general: {
520
+ class: 'PascalCase',
521
+ method: 'camelCase',
522
+ variable: 'camelCase',
523
+ constant: 'SCREAMING_SNAKE_CASE',
524
+ },
525
+ };
526
+ }
527
+ printSummary(config) {
528
+ this.print(`\n${c.green}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}`);
529
+ this.print(`${c.green} PROFILE SUMMARY${c.reset}`);
530
+ this.print(`${c.green}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}\n`);
531
+ this.print(`${c.bright}Name:${c.reset} ${config.name}`);
532
+ this.print(`${c.bright}Description:${c.reset} ${config.description}`);
533
+ this.print('');
534
+ this.print(`${c.bright}Architecture:${c.reset} ${config.architecture.type}`);
535
+ this.print(`${c.bright}DDD:${c.reset} ${config.ddd.enabled ? 'Enabled' : 'Disabled'}`);
536
+ this.print(`${c.bright}CQRS:${c.reset} ${config.cqrs.enabled ? `Enabled (${config.cqrs.separation})` : 'Disabled'}`);
537
+ this.print(`${c.bright}Event-Driven:${c.reset} ${config.eventDriven.enabled ? `Enabled (${config.eventDriven.approach})` : 'Disabled'}`);
538
+ this.print('');
539
+ this.print(`${c.bright}Code Quality:${c.reset}`);
540
+ this.print(` - Max method lines: ${config.codeQuality.maxMethodLines}`);
541
+ this.print(` - Max class lines: ${config.codeQuality.maxClassLines}`);
542
+ this.print(` - Min test coverage: ${config.codeQuality.minimumTestCoverage}%`);
543
+ this.print('');
544
+ this.print(`${c.bright}Testing:${c.reset} ${config.testing.framework} + ${config.testing.assertionLibrary}`);
545
+ this.print(`\n${c.green}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}\n`);
546
+ }
547
+ async run() {
548
+ this.print(`
549
+ ${c.cyan}╔═══════════════════════════════════════════════════════════╗
550
+ ║ ║
551
+ ║ ${c.bright}🔧 CORBAT PROFILE GENERATOR${c.reset}${c.cyan} ║
552
+ ║ ║
553
+ ║ Create a custom coding standards profile for your ║
554
+ ║ project in seconds. ║
555
+ ║ ║
556
+ ╚═══════════════════════════════════════════════════════════╝${c.reset}
557
+ `);
558
+ // Detect project
559
+ await this.detectProject();
560
+ if (this.detectedInfo.language !== 'unknown') {
561
+ this.printDetectedInfo();
562
+ const useDetected = await this.confirm('Use detected configuration as base?', true);
563
+ if (!useDetected) {
564
+ this.detectedInfo = { language: 'unknown' };
565
+ }
566
+ }
567
+ else {
568
+ this.print(`${c.yellow}⚠ Could not auto-detect project type.${c.reset}`);
569
+ this.print(`${c.dim} Starting from scratch...${c.reset}\n`);
570
+ const lang = await this.select('Select your primary language:', ['Java', 'TypeScript', 'JavaScript', 'Python', 'Other'], 0);
571
+ this.detectedInfo.language = lang;
572
+ }
573
+ // Run wizard
574
+ const config = await this.runWizard();
575
+ // Show summary
576
+ this.printSummary(config);
577
+ // Confirm and save
578
+ const shouldSave = await this.confirm('Save this profile?', true);
579
+ if (shouldSave) {
580
+ const filename = `${config.name.toLowerCase().replace(/\s+/g, '-')}.yaml`;
581
+ const saveLocation = await this.select('Where to save?', [`profiles/custom/${filename} (corbat-mcp)`, `.corbat/${filename} (project local)`, 'Custom path...'], 0);
582
+ let savePath;
583
+ if (saveLocation.includes('profiles/custom')) {
584
+ // Save to corbat-mcp profiles
585
+ const corbatRoot = join(__dirname, '..', '..');
586
+ savePath = join(corbatRoot, 'profiles', 'custom', filename);
587
+ }
588
+ else if (saveLocation.includes('.corbat')) {
589
+ // Save to project local
590
+ const corbatDir = join(this.projectDir, '.corbat');
591
+ try {
592
+ await access(corbatDir);
593
+ }
594
+ catch {
595
+ const { mkdir } = await import('node:fs/promises');
596
+ await mkdir(corbatDir, { recursive: true });
597
+ }
598
+ savePath = join(corbatDir, filename);
599
+ }
600
+ else {
601
+ // Custom path
602
+ const customPath = await this.input('Enter full path');
603
+ savePath = customPath;
604
+ }
605
+ const yamlContent = this.buildYamlProfile(config);
606
+ await writeFile(savePath, yamlContent, 'utf-8');
607
+ this.print(`\n${c.green}✓ Profile saved to: ${savePath}${c.reset}`);
608
+ // Also create .corbat.json if desired
609
+ const createCorbatJson = await this.confirm('\nCreate .corbat.json in project root?', true);
610
+ if (createCorbatJson) {
611
+ const corbatJson = {
612
+ profile: config.name.toLowerCase().replace(/\s+/g, '-'),
613
+ autoInject: true,
614
+ rules: {
615
+ always: config.ddd.enabled
616
+ ? ['Follow DDD patterns', 'Use ubiquitous language']
617
+ : ['Follow clean code principles'],
618
+ },
619
+ decisions: {},
620
+ };
621
+ await writeFile(join(this.projectDir, '.corbat.json'), JSON.stringify(corbatJson, null, 2), 'utf-8');
622
+ this.print(`${c.green}✓ Created .corbat.json${c.reset}`);
623
+ }
624
+ this.print(`
625
+ ${c.green}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
626
+ ${c.bright} 🎉 All done!${c.reset}
627
+
628
+ ${c.cyan}Next steps:${c.reset}
629
+
630
+ 1. Use your profile:
631
+ ${c.dim}"Review this code using corbat with profile ${config.name.toLowerCase().replace(/\s+/g, '-')}"${c.reset}
632
+
633
+ 2. Or with @corbat shortcut:
634
+ ${c.dim}"Create a service @corbat"${c.reset}
635
+
636
+ ${c.green}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
637
+ `);
638
+ }
639
+ else {
640
+ this.print(`\n${c.yellow}Profile not saved.${c.reset}`);
641
+ }
642
+ this.rl.close();
643
+ }
644
+ }
645
+ // Run if executed directly
646
+ const init = new CorbatInit();
647
+ init.run().catch((error) => {
648
+ console.error(`${colors.red}Error: ${error.message}${colors.reset}`);
649
+ process.exit(1);
650
+ });
651
+ //# sourceMappingURL=init.js.map