antigravity-flow 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 (115) hide show
  1. package/README.md +182 -0
  2. package/dist/bin/ag-flow.js +127 -0
  3. package/dist/src/adapters/ConsoleLoggerAdapter.js +52 -0
  4. package/dist/src/adapters/EjsTemplateAdapter.js +66 -0
  5. package/dist/src/adapters/JsonLocalizationAdapter.js +81 -0
  6. package/dist/src/adapters/NodeFileSystemAdapter.js +52 -0
  7. package/dist/src/adapters/index.js +20 -0
  8. package/dist/src/commands/guide.js +89 -0
  9. package/dist/src/commands/index.js +19 -0
  10. package/dist/src/commands/init.js +581 -0
  11. package/dist/src/commands/templates.js +143 -0
  12. package/dist/src/core/ConfigService.js +75 -0
  13. package/dist/src/core/ContextGenerator.js +27 -0
  14. package/dist/src/core/DockerfileGenerator.js +151 -0
  15. package/dist/src/core/GitignoreGenerator.js +129 -0
  16. package/dist/src/core/PipelineGenerator.js +68 -0
  17. package/dist/src/core/RulesComposer.js +62 -0
  18. package/dist/src/core/StackDetector.js +115 -0
  19. package/dist/src/core/WorkflowGenerator.js +137 -0
  20. package/dist/src/core/index.js +26 -0
  21. package/dist/src/core/interfaces.js +2 -0
  22. package/dist/src/core/pipelines/PipelineStrategy.js +97 -0
  23. package/dist/src/core/types.js +300 -0
  24. package/dist/src/locales/en.json +293 -0
  25. package/dist/src/locales/fr.json +293 -0
  26. package/dist/src/templates/docker/django.Dockerfile.ejs +21 -0
  27. package/dist/src/templates/docker/dockerignore.ejs +12 -0
  28. package/dist/src/templates/docker/go.Dockerfile.ejs +20 -0
  29. package/dist/src/templates/docker/nestjs.Dockerfile.ejs +25 -0
  30. package/dist/src/templates/docker/node.Dockerfile.ejs +13 -0
  31. package/dist/src/templates/docker/python.Dockerfile.ejs +13 -0
  32. package/dist/src/templates/docker/springboot.Dockerfile.ejs +25 -0
  33. package/dist/src/templates/en/architect.md.ejs +85 -0
  34. package/dist/src/templates/en/ba.md.ejs +88 -0
  35. package/dist/src/templates/en/data.md.ejs +47 -0
  36. package/dist/src/templates/en/dev.md.ejs +77 -0
  37. package/dist/src/templates/en/devops.md.ejs +54 -0
  38. package/dist/src/templates/en/fragments/arch/feature-sliced.md.ejs +12 -0
  39. package/dist/src/templates/en/fragments/arch/hexagonal.md.ejs +14 -0
  40. package/dist/src/templates/en/fragments/arch/mvc.md.ejs +11 -0
  41. package/dist/src/templates/en/fragments/aspnet-core.md.ejs +11 -0
  42. package/dist/src/templates/en/fragments/base-rules.md.ejs +5 -0
  43. package/dist/src/templates/en/fragments/django.md.ejs +12 -0
  44. package/dist/src/templates/en/fragments/fastapi.md.ejs +11 -0
  45. package/dist/src/templates/en/fragments/flutter.md.ejs +11 -0
  46. package/dist/src/templates/en/fragments/nestjs.md.ejs +5 -0
  47. package/dist/src/templates/en/fragments/nextjs.md.ejs +10 -0
  48. package/dist/src/templates/en/fragments/react-native.md.ejs +12 -0
  49. package/dist/src/templates/en/fragments/react.md.ejs +5 -0
  50. package/dist/src/templates/en/fragments/rigor/legacy.md.ejs +6 -0
  51. package/dist/src/templates/en/fragments/rigor/prototype.md.ejs +6 -0
  52. package/dist/src/templates/en/fragments/rigor/strict.md.ejs +7 -0
  53. package/dist/src/templates/en/fragments/rust.md.ejs +11 -0
  54. package/dist/src/templates/en/fragments/scrypto.md.ejs +8 -0
  55. package/dist/src/templates/en/fragments/solidity.md.ejs +20 -0
  56. package/dist/src/templates/en/fragments/spring-boot.md.ejs +13 -0
  57. package/dist/src/templates/en/guide.txt +48 -0
  58. package/dist/src/templates/en/lead.md.ejs +76 -0
  59. package/dist/src/templates/en/pipelines/github-dotnet.yml.ejs +24 -0
  60. package/dist/src/templates/en/pipelines/github-flutter.yml.ejs +24 -0
  61. package/dist/src/templates/en/pipelines/github-nestjs.yml.ejs +26 -0
  62. package/dist/src/templates/en/pipelines/github-react.yml.ejs +26 -0
  63. package/dist/src/templates/en/pipelines/github-rust.yml.ejs +38 -0
  64. package/dist/src/templates/en/pipelines/github-scrypto.yml.ejs +34 -0
  65. package/dist/src/templates/en/po.md.ejs +86 -0
  66. package/dist/src/templates/en/project-context.md.ejs +26 -0
  67. package/dist/src/templates/en/qa.md.ejs +103 -0
  68. package/dist/src/templates/en/rules.md.ejs +30 -0
  69. package/dist/src/templates/en/security.md.ejs +55 -0
  70. package/dist/src/templates/en/techwriter.md.ejs +46 -0
  71. package/dist/src/templates/fr/architect.md.ejs +15 -0
  72. package/dist/src/templates/fr/ba.md.ejs +11 -0
  73. package/dist/src/templates/fr/data.md.ejs +47 -0
  74. package/dist/src/templates/fr/dev.md.ejs +11 -0
  75. package/dist/src/templates/fr/devops.md.ejs +54 -0
  76. package/dist/src/templates/fr/fragments/arch/feature-sliced.md.ejs +12 -0
  77. package/dist/src/templates/fr/fragments/arch/hexagonal.md.ejs +14 -0
  78. package/dist/src/templates/fr/fragments/arch/mvc.md.ejs +11 -0
  79. package/dist/src/templates/fr/fragments/aspnet-core.md.ejs +11 -0
  80. package/dist/src/templates/fr/fragments/flutter.md.ejs +11 -0
  81. package/dist/src/templates/fr/fragments/rigor/legacy.md.ejs +6 -0
  82. package/dist/src/templates/fr/fragments/rigor/prototype.md.ejs +6 -0
  83. package/dist/src/templates/fr/fragments/rigor/strict.md.ejs +7 -0
  84. package/dist/src/templates/fr/fragments/rust.md.ejs +10 -0
  85. package/dist/src/templates/fr/fragments/scrypto.md.ejs +8 -0
  86. package/dist/src/templates/fr/guide.txt +47 -0
  87. package/dist/src/templates/fr/lead.md.ejs +12 -0
  88. package/dist/src/templates/fr/po.md.ejs +11 -0
  89. package/dist/src/templates/fr/project-context.md.ejs +28 -0
  90. package/dist/src/templates/fr/qa.md.ejs +14 -0
  91. package/dist/src/templates/fr/rules.md.ejs +30 -0
  92. package/dist/src/templates/fr/security.md.ejs +55 -0
  93. package/dist/src/templates/fr/techwriter.md.ejs +46 -0
  94. package/dist/src/templates/gitignore/dotnet.txt +6 -0
  95. package/dist/src/templates/gitignore/go.txt +15 -0
  96. package/dist/src/templates/gitignore/java.txt +4 -0
  97. package/dist/src/templates/gitignore/jetbrains.txt +4 -0
  98. package/dist/src/templates/gitignore/linux.txt +5 -0
  99. package/dist/src/templates/gitignore/macos.txt +3 -0
  100. package/dist/src/templates/gitignore/node.txt +11 -0
  101. package/dist/src/templates/gitignore/python.txt +10 -0
  102. package/dist/src/templates/gitignore/rust.txt +3 -0
  103. package/dist/src/templates/gitignore/solidity.txt +19 -0
  104. package/dist/src/templates/gitignore/vscode.txt +6 -0
  105. package/dist/src/templates/gitignore/windows.txt +4 -0
  106. package/dist/tests/commands/InitCommand.spec.js +529 -0
  107. package/dist/tests/commands/InitConfig.spec.js +108 -0
  108. package/dist/tests/core/ConfigService.spec.js +97 -0
  109. package/dist/tests/core/ContextGenerator.spec.js +51 -0
  110. package/dist/tests/core/PipelineGenerator.spec.js +206 -0
  111. package/dist/tests/core/RulesComposer.spec.js +89 -0
  112. package/dist/tests/core/StackDetector.spec.js +69 -0
  113. package/dist/tests/core/Types.spec.js +243 -0
  114. package/dist/tests/core/WorkflowGenerator.spec.js +99 -0
  115. package/package.json +55 -0
@@ -0,0 +1,529 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const init_1 = require("../../src/commands/init");
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ const types_1 = require("../../src/core/types");
9
+ jest.mock('inquirer');
10
+ describe('InitCommand', () => {
11
+ let initCommand;
12
+ let mockWorkflowGenerator;
13
+ let mockLogger;
14
+ let mockLocalizationService;
15
+ let mockPipelineGenerator;
16
+ let mockConfigService;
17
+ let mockStackDetector;
18
+ let mockGitignoreGenerator;
19
+ let mockDockerfileGenerator;
20
+ let mockFileSystem;
21
+ // Helper to create mock answers for all prompt sections
22
+ const createMockAnswers = (overrides = {}) => {
23
+ const defaults = {
24
+ // Language
25
+ language: 'en',
26
+ // Core
27
+ projectDescription: 'Test project',
28
+ frontend: types_1.FrontendFramework.REACT,
29
+ backend: types_1.BackendFramework.NESTJS,
30
+ smartContract: types_1.SmartContractFramework.NONE,
31
+ database: types_1.DatabaseType.POSTGRESQL,
32
+ orm: types_1.OrmType.PRISMA,
33
+ architecture: types_1.ArchitectureType.HEXAGONAL,
34
+ rigor: types_1.RigorMode.STRICT,
35
+ // VCS
36
+ versionControl: types_1.VersionControlPlatform.GITHUB,
37
+ branchingStrategy: types_1.BranchingStrategy.GITHUB_FLOW,
38
+ commitConvention: types_1.CommitConvention.CONVENTIONAL,
39
+ // Tooling
40
+ packageManager: types_1.PackageManager.NPM,
41
+ testingFramework: types_1.TestingFramework.JEST,
42
+ coverageTarget: types_1.CoverageTarget.FULL,
43
+ // Monorepo
44
+ isMonorepo: false,
45
+ monorepoTool: types_1.MonorepoTool.NONE,
46
+ apps: [],
47
+ // Deployment
48
+ deploymentPlatform: types_1.DeploymentPlatform.VERCEL,
49
+ containerization: types_1.Containerization.NONE,
50
+ securityScanning: [types_1.SecurityScanningTool.DEPENDABOT],
51
+ // Commands
52
+ buildCommand: 'npm run build',
53
+ testCommand: 'npm test',
54
+ // Strict mode conditionals
55
+ prApprovalsRequired: 2,
56
+ preCommitHooks: true,
57
+ blockMergeOnFailingChecks: true,
58
+ // Hexagonal conditionals
59
+ interfacePrefix: 'I',
60
+ repositoryPattern: 'repository',
61
+ // Final
62
+ roles: [types_1.WorkflowRole.DEVELOPER, types_1.WorkflowRole.QA],
63
+ ideIntegration: [],
64
+ confirm: true,
65
+ };
66
+ return { ...defaults, ...overrides };
67
+ };
68
+ beforeEach(() => {
69
+ mockWorkflowGenerator = {
70
+ generateWorkflows: jest.fn(),
71
+ generateProjectContext: jest.fn(),
72
+ };
73
+ mockLogger = {
74
+ info: jest.fn(),
75
+ success: jest.fn(),
76
+ error: jest.fn(),
77
+ warn: jest.fn(),
78
+ };
79
+ mockLocalizationService = {
80
+ setLanguage: jest.fn(),
81
+ getLanguage: jest.fn(),
82
+ translate: jest.fn((key) => key),
83
+ };
84
+ mockPipelineGenerator = {
85
+ generatePipeline: jest.fn(),
86
+ };
87
+ mockConfigService = {
88
+ saveConfig: jest.fn(),
89
+ loadConfig: jest.fn(),
90
+ };
91
+ mockStackDetector = {
92
+ detectStack: jest.fn().mockResolvedValue({}),
93
+ };
94
+ mockGitignoreGenerator = {
95
+ generate: jest.fn().mockResolvedValue(undefined),
96
+ };
97
+ mockDockerfileGenerator = {
98
+ generate: jest.fn().mockResolvedValue(undefined),
99
+ };
100
+ mockFileSystem = {
101
+ exists: jest.fn().mockResolvedValue(false),
102
+ readFile: jest.fn().mockResolvedValue('Rule content'),
103
+ writeFile: jest.fn(),
104
+ createDirectory: jest.fn(),
105
+ };
106
+ initCommand = new init_1.InitCommand(mockWorkflowGenerator, mockLogger, mockLocalizationService, mockPipelineGenerator, mockConfigService, mockStackDetector, mockGitignoreGenerator, mockDockerfileGenerator, mockFileSystem);
107
+ });
108
+ describe('Basic Execution', () => {
109
+ it('should execute successfully with default configuration', async () => {
110
+ const answers = createMockAnswers();
111
+ inquirer_1.default.prompt.mockResolvedValue(answers);
112
+ await initCommand.execute();
113
+ expect(mockLocalizationService.setLanguage).toHaveBeenCalledWith('en');
114
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalled();
115
+ expect(mockWorkflowGenerator.generateProjectContext).toHaveBeenCalled();
116
+ expect(mockPipelineGenerator.generatePipeline).toHaveBeenCalled();
117
+ expect(mockConfigService.saveConfig).toHaveBeenCalled();
118
+ expect(mockLogger.success).toHaveBeenCalledWith(expect.stringContaining('prompts.success'));
119
+ });
120
+ it('should cancel when user does not confirm', async () => {
121
+ const answers = createMockAnswers({ confirm: false });
122
+ inquirer_1.default.prompt.mockResolvedValue(answers);
123
+ await initCommand.execute();
124
+ expect(mockLogger.warn).toHaveBeenCalledWith('prompts.cancelled');
125
+ expect(mockWorkflowGenerator.generateWorkflows).not.toHaveBeenCalled();
126
+ });
127
+ it('should log error on failure', async () => {
128
+ inquirer_1.default.prompt.mockRejectedValue(new Error('Test Error'));
129
+ await initCommand.execute();
130
+ expect(mockLogger.error).toHaveBeenCalledWith('prompts.failed');
131
+ });
132
+ });
133
+ describe('New Roles', () => {
134
+ it('should support DevOps role', async () => {
135
+ const answers = createMockAnswers({ roles: [types_1.WorkflowRole.DEVOPS] });
136
+ inquirer_1.default.prompt.mockResolvedValue(answers);
137
+ await initCommand.execute();
138
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.anything(), expect.arrayContaining([types_1.WorkflowRole.DEVOPS]));
139
+ });
140
+ it('should support Security Engineer role', async () => {
141
+ const answers = createMockAnswers({ roles: [types_1.WorkflowRole.SECURITY] });
142
+ inquirer_1.default.prompt.mockResolvedValue(answers);
143
+ await initCommand.execute();
144
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.anything(), expect.arrayContaining([types_1.WorkflowRole.SECURITY]));
145
+ });
146
+ it('should support Tech Writer role', async () => {
147
+ const answers = createMockAnswers({ roles: [types_1.WorkflowRole.TECH_WRITER] });
148
+ inquirer_1.default.prompt.mockResolvedValue(answers);
149
+ await initCommand.execute();
150
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.anything(), expect.arrayContaining([types_1.WorkflowRole.TECH_WRITER]));
151
+ });
152
+ it('should support Data Engineer role', async () => {
153
+ const answers = createMockAnswers({ roles: [types_1.WorkflowRole.DATA_ENGINEER] });
154
+ inquirer_1.default.prompt.mockResolvedValue(answers);
155
+ await initCommand.execute();
156
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.anything(), expect.arrayContaining([types_1.WorkflowRole.DATA_ENGINEER]));
157
+ });
158
+ it('should support multiple new roles together', async () => {
159
+ const allNewRoles = [
160
+ types_1.WorkflowRole.DEVOPS,
161
+ types_1.WorkflowRole.SECURITY,
162
+ types_1.WorkflowRole.TECH_WRITER,
163
+ types_1.WorkflowRole.DATA_ENGINEER,
164
+ ];
165
+ const answers = createMockAnswers({ roles: allNewRoles });
166
+ inquirer_1.default.prompt.mockResolvedValue(answers);
167
+ await initCommand.execute();
168
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.anything(), expect.arrayContaining(allNewRoles));
169
+ });
170
+ });
171
+ describe('Database Configuration', () => {
172
+ it('should support PostgreSQL database', async () => {
173
+ const answers = createMockAnswers({
174
+ database: types_1.DatabaseType.POSTGRESQL,
175
+ orm: types_1.OrmType.PRISMA,
176
+ });
177
+ inquirer_1.default.prompt.mockResolvedValue(answers);
178
+ await initCommand.execute();
179
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
180
+ techStack: expect.objectContaining({
181
+ database: types_1.DatabaseType.POSTGRESQL,
182
+ orm: types_1.OrmType.PRISMA,
183
+ }),
184
+ }), expect.anything());
185
+ });
186
+ it('should support SQL Server database', async () => {
187
+ const answers = createMockAnswers({
188
+ database: types_1.DatabaseType.SQLSERVER,
189
+ orm: types_1.OrmType.ENTITY_FRAMEWORK,
190
+ });
191
+ inquirer_1.default.prompt.mockResolvedValue(answers);
192
+ await initCommand.execute();
193
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
194
+ techStack: expect.objectContaining({
195
+ database: types_1.DatabaseType.SQLSERVER,
196
+ }),
197
+ }), expect.anything());
198
+ });
199
+ it('should support Oracle database', async () => {
200
+ const answers = createMockAnswers({
201
+ database: types_1.DatabaseType.ORACLE,
202
+ orm: types_1.OrmType.TYPEORM,
203
+ });
204
+ inquirer_1.default.prompt.mockResolvedValue(answers);
205
+ await initCommand.execute();
206
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
207
+ techStack: expect.objectContaining({
208
+ database: types_1.DatabaseType.ORACLE,
209
+ }),
210
+ }), expect.anything());
211
+ });
212
+ it('should support MongoDB with Mongoose', async () => {
213
+ const answers = createMockAnswers({
214
+ database: types_1.DatabaseType.MONGODB,
215
+ orm: types_1.OrmType.MONGOOSE,
216
+ });
217
+ inquirer_1.default.prompt.mockResolvedValue(answers);
218
+ await initCommand.execute();
219
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
220
+ techStack: expect.objectContaining({
221
+ database: types_1.DatabaseType.MONGODB,
222
+ orm: types_1.OrmType.MONGOOSE,
223
+ }),
224
+ }), expect.anything());
225
+ });
226
+ });
227
+ describe('Framework Configuration', () => {
228
+ it('should support Next.js frontend', async () => {
229
+ const answers = createMockAnswers({ frontend: types_1.FrontendFramework.NEXTJS });
230
+ inquirer_1.default.prompt.mockResolvedValue(answers);
231
+ await initCommand.execute();
232
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
233
+ techStack: expect.objectContaining({
234
+ frontend: types_1.FrontendFramework.NEXTJS,
235
+ }),
236
+ }), expect.anything());
237
+ });
238
+ it('should support FastAPI backend', async () => {
239
+ const answers = createMockAnswers({
240
+ backend: types_1.BackendFramework.FASTAPI,
241
+ packageManager: types_1.PackageManager.POETRY,
242
+ });
243
+ inquirer_1.default.prompt.mockResolvedValue(answers);
244
+ await initCommand.execute();
245
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
246
+ techStack: expect.objectContaining({
247
+ backend: types_1.BackendFramework.FASTAPI,
248
+ }),
249
+ }), expect.anything());
250
+ });
251
+ it('should support Spring Boot backend', async () => {
252
+ const answers = createMockAnswers({
253
+ backend: types_1.BackendFramework.SPRING_BOOT,
254
+ packageManager: types_1.PackageManager.MAVEN,
255
+ });
256
+ inquirer_1.default.prompt.mockResolvedValue(answers);
257
+ await initCommand.execute();
258
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
259
+ techStack: expect.objectContaining({
260
+ backend: types_1.BackendFramework.SPRING_BOOT,
261
+ }),
262
+ }), expect.anything());
263
+ });
264
+ it('should support Solidity smart contracts', async () => {
265
+ const answers = createMockAnswers({
266
+ smartContract: types_1.SmartContractFramework.SOLIDITY,
267
+ });
268
+ inquirer_1.default.prompt.mockResolvedValue(answers);
269
+ await initCommand.execute();
270
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
271
+ techStack: expect.objectContaining({
272
+ smartContract: types_1.SmartContractFramework.SOLIDITY,
273
+ }),
274
+ }), expect.anything());
275
+ });
276
+ });
277
+ describe('Version Control Configuration', () => {
278
+ it('should configure GitHub with GitHub Flow', async () => {
279
+ const answers = createMockAnswers({
280
+ versionControl: types_1.VersionControlPlatform.GITHUB,
281
+ branchingStrategy: types_1.BranchingStrategy.GITHUB_FLOW,
282
+ commitConvention: types_1.CommitConvention.CONVENTIONAL,
283
+ });
284
+ inquirer_1.default.prompt.mockResolvedValue(answers);
285
+ await initCommand.execute();
286
+ expect(mockConfigService.saveConfig).toHaveBeenCalledWith(expect.objectContaining({
287
+ versionControl: types_1.VersionControlPlatform.GITHUB,
288
+ branchingStrategy: types_1.BranchingStrategy.GITHUB_FLOW,
289
+ commitConvention: types_1.CommitConvention.CONVENTIONAL,
290
+ }));
291
+ });
292
+ it('should configure GitLab with GitFlow', async () => {
293
+ const answers = createMockAnswers({
294
+ versionControl: types_1.VersionControlPlatform.GITLAB,
295
+ branchingStrategy: types_1.BranchingStrategy.GITFLOW,
296
+ });
297
+ inquirer_1.default.prompt.mockResolvedValue(answers);
298
+ await initCommand.execute();
299
+ expect(mockConfigService.saveConfig).toHaveBeenCalledWith(expect.objectContaining({
300
+ versionControl: types_1.VersionControlPlatform.GITLAB,
301
+ branchingStrategy: types_1.BranchingStrategy.GITFLOW,
302
+ }));
303
+ });
304
+ it('should support Azure DevOps', async () => {
305
+ const answers = createMockAnswers({
306
+ versionControl: types_1.VersionControlPlatform.AZURE_DEVOPS,
307
+ });
308
+ inquirer_1.default.prompt.mockResolvedValue(answers);
309
+ await initCommand.execute();
310
+ expect(mockConfigService.saveConfig).toHaveBeenCalledWith(expect.objectContaining({
311
+ versionControl: types_1.VersionControlPlatform.AZURE_DEVOPS,
312
+ }));
313
+ });
314
+ });
315
+ describe('Deployment Configuration', () => {
316
+ it('should configure Vercel deployment', async () => {
317
+ const answers = createMockAnswers({
318
+ deploymentPlatform: types_1.DeploymentPlatform.VERCEL,
319
+ });
320
+ inquirer_1.default.prompt.mockResolvedValue(answers);
321
+ await initCommand.execute();
322
+ expect(mockConfigService.saveConfig).toHaveBeenCalledWith(expect.objectContaining({
323
+ deploymentPlatform: types_1.DeploymentPlatform.VERCEL,
324
+ }));
325
+ });
326
+ it('should configure Docker with Kubernetes', async () => {
327
+ const answers = createMockAnswers({
328
+ deploymentPlatform: types_1.DeploymentPlatform.AWS,
329
+ containerization: types_1.Containerization.KUBERNETES,
330
+ });
331
+ inquirer_1.default.prompt.mockResolvedValue(answers);
332
+ await initCommand.execute();
333
+ expect(mockConfigService.saveConfig).toHaveBeenCalledWith(expect.objectContaining({
334
+ deploymentPlatform: types_1.DeploymentPlatform.AWS,
335
+ containerization: types_1.Containerization.KUBERNETES,
336
+ }));
337
+ });
338
+ it('should configure security scanning tools', async () => {
339
+ const answers = createMockAnswers({
340
+ securityScanning: [types_1.SecurityScanningTool.SNYK, types_1.SecurityScanningTool.SONARQUBE],
341
+ });
342
+ inquirer_1.default.prompt.mockResolvedValue(answers);
343
+ await initCommand.execute();
344
+ expect(mockConfigService.saveConfig).toHaveBeenCalledWith(expect.objectContaining({
345
+ securityScanning: expect.arrayContaining([
346
+ types_1.SecurityScanningTool.SNYK,
347
+ types_1.SecurityScanningTool.SONARQUBE,
348
+ ]),
349
+ }));
350
+ });
351
+ });
352
+ describe('Monorepo Configuration', () => {
353
+ it('should configure Turborepo for monorepo', async () => {
354
+ const answers = createMockAnswers({
355
+ isMonorepo: true,
356
+ monorepoTool: types_1.MonorepoTool.TURBOREPO,
357
+ apps: ['api', 'web', 'mobile'],
358
+ });
359
+ inquirer_1.default.prompt.mockResolvedValue(answers);
360
+ await initCommand.execute();
361
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
362
+ isMonorepo: true,
363
+ monorepoTool: types_1.MonorepoTool.TURBOREPO,
364
+ apps: ['api', 'web', 'mobile'],
365
+ }), expect.anything());
366
+ });
367
+ it('should configure Nx for monorepo', async () => {
368
+ const answers = createMockAnswers({
369
+ isMonorepo: true,
370
+ monorepoTool: types_1.MonorepoTool.NX,
371
+ apps: ['backend', 'frontend'],
372
+ });
373
+ inquirer_1.default.prompt.mockResolvedValue(answers);
374
+ await initCommand.execute();
375
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
376
+ monorepoTool: types_1.MonorepoTool.NX,
377
+ }), expect.anything());
378
+ });
379
+ });
380
+ describe('IDE Integration', () => {
381
+ it('should configure Cursor integration', async () => {
382
+ const answers = createMockAnswers({
383
+ roles: [types_1.WorkflowRole.DEVELOPER],
384
+ ideIntegration: [types_1.IdeIntegration.CURSOR],
385
+ });
386
+ inquirer_1.default.prompt.mockResolvedValue(answers);
387
+ mockFileSystem.exists.mockResolvedValue(true);
388
+ await initCommand.execute();
389
+ expect(mockLogger.success).toHaveBeenCalledWith(expect.stringContaining('Cursor'));
390
+ expect(mockFileSystem.writeFile).toHaveBeenCalled();
391
+ });
392
+ it('should configure Gemini Code Assist integration', async () => {
393
+ const answers = createMockAnswers({
394
+ ideIntegration: [types_1.IdeIntegration.GEMINI],
395
+ });
396
+ inquirer_1.default.prompt.mockResolvedValue(answers);
397
+ await initCommand.execute();
398
+ expect(mockLogger.success).toHaveBeenCalledWith(expect.stringContaining('Gemini Code Assist'));
399
+ expect(mockFileSystem.writeFile).toHaveBeenCalled();
400
+ });
401
+ it('should configure multiple IDE integrations', async () => {
402
+ const answers = createMockAnswers({
403
+ roles: [types_1.WorkflowRole.DEVELOPER],
404
+ ideIntegration: [types_1.IdeIntegration.CURSOR, types_1.IdeIntegration.WINDSURF, types_1.IdeIntegration.GEMINI],
405
+ });
406
+ inquirer_1.default.prompt.mockResolvedValue(answers);
407
+ mockFileSystem.exists.mockResolvedValue(true);
408
+ await initCommand.execute();
409
+ expect(mockLogger.success).toHaveBeenCalledWith(expect.stringContaining('Cursor'));
410
+ expect(mockLogger.success).toHaveBeenCalledWith(expect.stringContaining('Windsurf'));
411
+ expect(mockLogger.success).toHaveBeenCalledWith(expect.stringContaining('Gemini'));
412
+ });
413
+ });
414
+ describe('Rigor Modes', () => {
415
+ it('should configure strict mode with all options', async () => {
416
+ const answers = createMockAnswers({
417
+ rigor: types_1.RigorMode.STRICT,
418
+ prApprovalsRequired: 2,
419
+ preCommitHooks: true,
420
+ blockMergeOnFailingChecks: true,
421
+ });
422
+ inquirer_1.default.prompt.mockResolvedValue(answers);
423
+ await initCommand.execute();
424
+ expect(mockConfigService.saveConfig).toHaveBeenCalledWith(expect.objectContaining({
425
+ rigor: types_1.RigorMode.STRICT,
426
+ prApprovalsRequired: 2,
427
+ preCommitHooks: true,
428
+ blockMergeOnFailingChecks: true,
429
+ }));
430
+ });
431
+ it('should support standard rigor mode', async () => {
432
+ const answers = createMockAnswers({
433
+ rigor: types_1.RigorMode.STANDARD,
434
+ coverageTarget: types_1.CoverageTarget.HIGH,
435
+ });
436
+ inquirer_1.default.prompt.mockResolvedValue(answers);
437
+ await initCommand.execute();
438
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
439
+ rigor: types_1.RigorMode.STANDARD,
440
+ }), expect.anything());
441
+ });
442
+ it('should support prototype rigor mode', async () => {
443
+ const answers = createMockAnswers({
444
+ rigor: types_1.RigorMode.PROTOTYPE,
445
+ coverageTarget: types_1.CoverageTarget.MINIMAL,
446
+ });
447
+ inquirer_1.default.prompt.mockResolvedValue(answers);
448
+ await initCommand.execute();
449
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
450
+ rigor: types_1.RigorMode.PROTOTYPE,
451
+ }), expect.anything());
452
+ });
453
+ it('should support legacy rigor mode', async () => {
454
+ const answers = createMockAnswers({ rigor: types_1.RigorMode.LEGACY });
455
+ inquirer_1.default.prompt.mockResolvedValue(answers);
456
+ await initCommand.execute();
457
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
458
+ rigor: types_1.RigorMode.LEGACY,
459
+ }), expect.anything());
460
+ });
461
+ });
462
+ describe('Architecture Configuration', () => {
463
+ it('should configure hexagonal architecture with interface prefix', async () => {
464
+ const answers = createMockAnswers({
465
+ architecture: types_1.ArchitectureType.HEXAGONAL,
466
+ interfacePrefix: 'I',
467
+ repositoryPattern: 'repository',
468
+ });
469
+ inquirer_1.default.prompt.mockResolvedValue(answers);
470
+ await initCommand.execute();
471
+ expect(mockConfigService.saveConfig).toHaveBeenCalledWith(expect.objectContaining({
472
+ architecture: types_1.ArchitectureType.HEXAGONAL,
473
+ interfacePrefix: 'I',
474
+ repositoryPattern: 'repository',
475
+ }));
476
+ });
477
+ it('should configure clean architecture', async () => {
478
+ const answers = createMockAnswers({
479
+ architecture: types_1.ArchitectureType.CLEAN,
480
+ interfacePrefix: '',
481
+ repositoryPattern: 'dao',
482
+ });
483
+ inquirer_1.default.prompt.mockResolvedValue(answers);
484
+ await initCommand.execute();
485
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
486
+ architecture: types_1.ArchitectureType.CLEAN,
487
+ }), expect.anything());
488
+ });
489
+ it('should configure microservices architecture', async () => {
490
+ const answers = createMockAnswers({
491
+ architecture: types_1.ArchitectureType.MICROSERVICES,
492
+ });
493
+ inquirer_1.default.prompt.mockResolvedValue(answers);
494
+ await initCommand.execute();
495
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
496
+ architecture: types_1.ArchitectureType.MICROSERVICES,
497
+ }), expect.anything());
498
+ });
499
+ });
500
+ describe('Package Manager Support', () => {
501
+ it('should use correct defaults for pnpm', async () => {
502
+ const answers = createMockAnswers({
503
+ packageManager: types_1.PackageManager.PNPM,
504
+ buildCommand: 'pnpm build',
505
+ testCommand: 'pnpm test',
506
+ });
507
+ inquirer_1.default.prompt.mockResolvedValue(answers);
508
+ await initCommand.execute();
509
+ expect(mockConfigService.saveConfig).toHaveBeenCalledWith(expect.objectContaining({
510
+ packageManager: types_1.PackageManager.PNPM,
511
+ buildCommand: 'pnpm build',
512
+ testCommand: 'pnpm test',
513
+ }));
514
+ });
515
+ it('should use correct defaults for cargo (Rust)', async () => {
516
+ const answers = createMockAnswers({
517
+ backend: types_1.BackendFramework.RUST,
518
+ packageManager: types_1.PackageManager.CARGO,
519
+ buildCommand: 'cargo build',
520
+ testCommand: 'cargo test',
521
+ });
522
+ inquirer_1.default.prompt.mockResolvedValue(answers);
523
+ await initCommand.execute();
524
+ expect(mockConfigService.saveConfig).toHaveBeenCalledWith(expect.objectContaining({
525
+ packageManager: types_1.PackageManager.CARGO,
526
+ }));
527
+ });
528
+ });
529
+ });
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const init_1 = require("../../src/commands/init");
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ const types_1 = require("../../src/core/types");
9
+ jest.mock('inquirer');
10
+ describe('InitCommand - Config Mode', () => {
11
+ let initCommand;
12
+ let mockWorkflowGenerator;
13
+ let mockLogger;
14
+ let mockLocalizationService;
15
+ let mockPipelineGenerator;
16
+ let mockConfigService;
17
+ let mockStackDetector;
18
+ let mockGitignoreGenerator;
19
+ let mockDockerfileGenerator;
20
+ let mockFileSystem;
21
+ beforeEach(() => {
22
+ mockWorkflowGenerator = {
23
+ generateWorkflows: jest.fn(),
24
+ generateProjectContext: jest.fn(),
25
+ };
26
+ mockLogger = {
27
+ info: jest.fn(),
28
+ success: jest.fn(),
29
+ error: jest.fn(),
30
+ warn: jest.fn(),
31
+ };
32
+ mockLocalizationService = {
33
+ setLanguage: jest.fn(),
34
+ getLanguage: jest.fn(),
35
+ translate: jest.fn((key) => key),
36
+ };
37
+ mockPipelineGenerator = {
38
+ generatePipeline: jest.fn(),
39
+ };
40
+ mockConfigService = {
41
+ saveConfig: jest.fn(),
42
+ loadConfig: jest.fn(),
43
+ };
44
+ mockStackDetector = {
45
+ detectStack: jest.fn().mockResolvedValue({}),
46
+ };
47
+ mockGitignoreGenerator = {
48
+ generate: jest.fn().mockResolvedValue(undefined),
49
+ };
50
+ mockDockerfileGenerator = {
51
+ generate: jest.fn().mockResolvedValue(undefined),
52
+ };
53
+ mockFileSystem = {
54
+ exists: jest.fn().mockResolvedValue(false),
55
+ readFile: jest.fn().mockResolvedValue(''),
56
+ writeFile: jest.fn(),
57
+ createDirectory: jest.fn(),
58
+ };
59
+ initCommand = new init_1.InitCommand(mockWorkflowGenerator, mockLogger, mockLocalizationService, mockPipelineGenerator, mockConfigService, mockStackDetector, mockGitignoreGenerator, mockDockerfileGenerator, mockFileSystem);
60
+ });
61
+ it('should load configuration from file and skip prompts', async () => {
62
+ const configPath = 'my-config.json';
63
+ const mockConfig = {
64
+ language: 'fr',
65
+ rootPath: '/test/path',
66
+ workflowDirectory: '.agent/workflows',
67
+ rulesDirectory: '.agent/rules',
68
+ techStack: {
69
+ frontend: types_1.FrontendFramework.VUE,
70
+ backend: types_1.BackendFramework.EXPRESS,
71
+ database: types_1.DatabaseType.MYSQL,
72
+ orm: types_1.OrmType.SEQUELIZE,
73
+ testingFramework: types_1.TestingFramework.JEST,
74
+ packageManager: types_1.PackageManager.YARN,
75
+ },
76
+ architecture: types_1.ArchitectureType.MVC,
77
+ rigor: types_1.RigorMode.STANDARD,
78
+ projectDescription: 'Configured Project',
79
+ isMonorepo: false,
80
+ apps: [],
81
+ versionControl: types_1.VersionControlPlatform.GITLAB,
82
+ deploymentPlatform: types_1.DeploymentPlatform.HEROKU,
83
+ roles: [types_1.WorkflowRole.PRODUCT_OWNER, types_1.WorkflowRole.ARCHITECT],
84
+ ideIntegrations: [types_1.IdeIntegration.WINDSURF],
85
+ };
86
+ mockFileSystem.exists.mockResolvedValue(true);
87
+ mockFileSystem.readFile.mockResolvedValue(JSON.stringify(mockConfig));
88
+ await initCommand.execute({ config: configPath });
89
+ // Should not prompt
90
+ expect(inquirer_1.default.prompt).not.toHaveBeenCalled();
91
+ // Should set language
92
+ expect(mockLocalizationService.setLanguage).toHaveBeenCalledWith('fr');
93
+ // Should generate files with config details
94
+ expect(mockWorkflowGenerator.generateWorkflows).toHaveBeenCalledWith(expect.objectContaining({
95
+ projectDescription: 'Configured Project',
96
+ architecture: types_1.ArchitectureType.MVC,
97
+ }), expect.arrayContaining([types_1.WorkflowRole.PRODUCT_OWNER, types_1.WorkflowRole.ARCHITECT]));
98
+ // Should integrate IDE
99
+ expect(mockFileSystem.writeFile).toHaveBeenCalledWith(expect.stringContaining('.windsurfrules'), expect.anything());
100
+ });
101
+ it('should handle missing config file gracefully', async () => {
102
+ mockFileSystem.exists.mockResolvedValue(false);
103
+ await initCommand.execute({ config: 'missing.json' });
104
+ expect(mockLogger.error).toHaveBeenCalledWith(expect.stringContaining('not found'));
105
+ expect(inquirer_1.default.prompt).not.toHaveBeenCalled();
106
+ expect(mockWorkflowGenerator.generateWorkflows).not.toHaveBeenCalled();
107
+ });
108
+ });