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,97 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const ConfigService_1 = require("../../src/core/ConfigService");
37
+ const types_1 = require("../../src/core/types");
38
+ const path = __importStar(require("path"));
39
+ describe('ConfigService', () => {
40
+ let configService;
41
+ let mockFileSystem;
42
+ let mockLogger;
43
+ const projectDetails = {
44
+ rootPath: '/test/root',
45
+ workflowDirectory: '.github/workflows',
46
+ rulesDirectory: '.agent/rules',
47
+ techStack: { frontend: types_1.FrontendFramework.NONE, backend: types_1.BackendFramework.NODE, smartContract: types_1.SmartContractFramework.NONE },
48
+ architecture: types_1.ArchitectureType.MVC,
49
+ rigor: types_1.RigorMode.STRICT,
50
+ buildCommand: 'npm run build',
51
+ testCommand: 'npm run test',
52
+ };
53
+ beforeEach(() => {
54
+ mockFileSystem = {
55
+ exists: jest.fn(),
56
+ readFile: jest.fn(),
57
+ writeFile: jest.fn(),
58
+ createDirectory: jest.fn(),
59
+ };
60
+ mockLogger = {
61
+ info: jest.fn(),
62
+ warn: jest.fn(),
63
+ error: jest.fn(),
64
+ success: jest.fn(),
65
+ };
66
+ configService = new ConfigService_1.ConfigService(mockFileSystem, mockLogger);
67
+ });
68
+ it('should save configuration successfully', async () => {
69
+ await configService.saveConfig(projectDetails);
70
+ const expectedPath = path.join(projectDetails.rootPath, '.agent/antigravity.json');
71
+ expect(mockFileSystem.createDirectory).toHaveBeenCalledWith(path.dirname(expectedPath));
72
+ expect(mockFileSystem.writeFile).toHaveBeenCalledWith(expectedPath, JSON.stringify(projectDetails, null, 2));
73
+ expect(mockLogger.success).toHaveBeenCalledWith(expect.stringContaining('Configuration saved'));
74
+ });
75
+ it('should load configuration if exists', async () => {
76
+ const expectedPath = path.join(process.cwd(), '.agent/antigravity.json');
77
+ mockFileSystem.exists.mockResolvedValue(true);
78
+ mockFileSystem.readFile.mockResolvedValue(JSON.stringify(projectDetails));
79
+ // Note: loadConfig currently uses process.cwd() as internal variable logic might differ.
80
+ // Let's check implementation behavior regarding rootPath argument.
81
+ // My implementation: async loadConfig(rootPath: string)
82
+ await configService.loadConfig('/test/loader');
83
+ const expectedCallPath = path.join('/test/loader', '.agent/antigravity.json');
84
+ expect(mockFileSystem.exists).toHaveBeenCalledWith(expectedCallPath);
85
+ expect(mockFileSystem.readFile).toHaveBeenCalledWith(expectedCallPath);
86
+ });
87
+ it('should return null if configuration does not exist', async () => {
88
+ mockFileSystem.exists.mockResolvedValue(false);
89
+ const result = await configService.loadConfig('/test/missing');
90
+ expect(result).toBeNull();
91
+ });
92
+ it('should handle errors during save', async () => {
93
+ mockFileSystem.writeFile.mockRejectedValue(new Error('Write Error'));
94
+ await configService.saveConfig(projectDetails);
95
+ expect(mockLogger.error).toHaveBeenCalledWith(expect.stringContaining('Failed to save configuration'));
96
+ });
97
+ });
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const ContextGenerator_1 = require("../../src/core/ContextGenerator");
4
+ const types_1 = require("../../src/core/types");
5
+ describe('ContextGenerator', () => {
6
+ let contextGenerator;
7
+ let mockTemplateProvider;
8
+ let mockLocalizationService;
9
+ beforeEach(() => {
10
+ mockTemplateProvider = {
11
+ getTemplate: jest.fn(),
12
+ render: jest.fn(),
13
+ };
14
+ mockLocalizationService = {
15
+ setLanguage: jest.fn(),
16
+ getLanguage: jest.fn().mockReturnValue('en'),
17
+ translate: jest.fn(),
18
+ };
19
+ contextGenerator = new ContextGenerator_1.ContextGenerator(mockTemplateProvider, mockLocalizationService);
20
+ });
21
+ it('should generate project context using the template', async () => {
22
+ const project = {
23
+ rootPath: '/test/root',
24
+ workflowDirectory: '.agent/workflows',
25
+ buildCommand: 'npm run build',
26
+ testCommand: 'npm test',
27
+ techStack: { frontend: types_1.FrontendFramework.REACT, backend: types_1.BackendFramework.NESTJS },
28
+ architecture: types_1.ArchitectureType.HEXAGONAL,
29
+ rigor: types_1.RigorMode.STRICT,
30
+ projectDescription: 'Test Project',
31
+ isMonorepo: true,
32
+ apps: ['app1', 'app2'],
33
+ };
34
+ const templateContent = 'Context Template';
35
+ const renderedContent = 'Rendered Context';
36
+ mockTemplateProvider.getTemplate.mockResolvedValue(templateContent);
37
+ mockTemplateProvider.render.mockReturnValue(renderedContent);
38
+ const result = await contextGenerator.generateContext(project);
39
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith('en/project-context.md.ejs');
40
+ expect(mockTemplateProvider.render).toHaveBeenCalledWith(templateContent, {
41
+ projectDescription: 'Test Project',
42
+ techStack: 'Frontend: react, Backend: nestjs',
43
+ architecture: 'hexagonal',
44
+ buildCommand: 'npm run build',
45
+ testCommand: 'npm test',
46
+ isMonorepo: true,
47
+ apps: ['app1', 'app2'],
48
+ });
49
+ expect(result).toBe(renderedContent);
50
+ });
51
+ });
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const PipelineGenerator_1 = require("../../src/core/PipelineGenerator");
37
+ const types_1 = require("../../src/core/types");
38
+ const path = __importStar(require("path"));
39
+ describe('PipelineGenerator', () => {
40
+ let pipelineGenerator;
41
+ let mockFileSystem;
42
+ let mockTemplateProvider;
43
+ let mockLogger;
44
+ beforeEach(() => {
45
+ mockFileSystem = {
46
+ createDirectory: jest.fn(),
47
+ exists: jest.fn(),
48
+ readFile: jest.fn(),
49
+ writeFile: jest.fn(),
50
+ };
51
+ mockTemplateProvider = {
52
+ getTemplate: jest.fn(),
53
+ render: jest.fn(),
54
+ };
55
+ mockLogger = {
56
+ info: jest.fn(),
57
+ warn: jest.fn(),
58
+ success: jest.fn(),
59
+ error: jest.fn(),
60
+ };
61
+ pipelineGenerator = new PipelineGenerator_1.PipelineGenerator(mockFileSystem, mockTemplateProvider, mockLogger);
62
+ });
63
+ it('should generate a React pipeline workflow', async () => {
64
+ const projectDetails = {
65
+ rootPath: '/test/root',
66
+ workflowDirectory: '.github/workflows',
67
+ rulesDirectory: '.agent/rules',
68
+ techStack: { frontend: types_1.FrontendFramework.REACT, backend: types_1.BackendFramework.NONE },
69
+ architecture: types_1.ArchitectureType.MVC,
70
+ rigor: types_1.RigorMode.STRICT,
71
+ buildCommand: 'npm run build',
72
+ testCommand: 'npm run test',
73
+ };
74
+ const templateContent = 'React Workflow Content';
75
+ mockTemplateProvider.getTemplate.mockResolvedValue(templateContent);
76
+ mockTemplateProvider.render.mockReturnValue(templateContent);
77
+ await pipelineGenerator.generatePipeline(projectDetails);
78
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('github-react.yml.ejs'));
79
+ expect(mockFileSystem.createDirectory).toHaveBeenCalledWith(projectDetails.workflowDirectory);
80
+ expect(mockFileSystem.writeFile).toHaveBeenCalledWith(path.join(projectDetails.workflowDirectory, 'ci.yml'), templateContent);
81
+ expect(mockLogger.success).toHaveBeenCalledWith('CI/CD Pipeline generated: .github/workflows/ci.yml');
82
+ });
83
+ it('should generate a Flutter pipeline workflow', async () => {
84
+ const projectDetails = {
85
+ rootPath: '/test/root',
86
+ workflowDirectory: '.github/workflows',
87
+ rulesDirectory: '.agent/rules',
88
+ techStack: { frontend: types_1.FrontendFramework.FLUTTER, backend: types_1.BackendFramework.NONE },
89
+ architecture: types_1.ArchitectureType.MVC,
90
+ rigor: types_1.RigorMode.PROTOTYPE,
91
+ buildCommand: 'flutter build apk',
92
+ testCommand: 'flutter test',
93
+ };
94
+ const templateContent = 'Flutter Workflow Content';
95
+ mockTemplateProvider.getTemplate.mockResolvedValue(templateContent);
96
+ mockTemplateProvider.render.mockReturnValue(templateContent);
97
+ await pipelineGenerator.generatePipeline(projectDetails);
98
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('github-flutter.yml.ejs'));
99
+ });
100
+ it('should generate a Scrypto pipeline workflow', async () => {
101
+ const projectDetails = {
102
+ rootPath: '/test/root',
103
+ workflowDirectory: '.github/workflows',
104
+ rulesDirectory: '.agent/rules',
105
+ techStack: { frontend: types_1.FrontendFramework.NONE, backend: types_1.BackendFramework.NONE, smartContract: types_1.SmartContractFramework.SCRYPTO },
106
+ architecture: types_1.ArchitectureType.MVC,
107
+ rigor: types_1.RigorMode.STRICT,
108
+ buildCommand: 'cargo build',
109
+ testCommand: 'cargo test',
110
+ };
111
+ mockTemplateProvider.getTemplate.mockResolvedValue('Scrypto Content');
112
+ await pipelineGenerator.generatePipeline(projectDetails);
113
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('github-scrypto.yml.ejs'));
114
+ });
115
+ it('should generate a NestJS pipeline workflow', async () => {
116
+ const projectDetails = {
117
+ rootPath: '/test/root',
118
+ workflowDirectory: '.github/workflows',
119
+ rulesDirectory: '.agent/rules',
120
+ techStack: { frontend: types_1.FrontendFramework.NONE, backend: types_1.BackendFramework.NESTJS },
121
+ architecture: types_1.ArchitectureType.HEXAGONAL,
122
+ rigor: types_1.RigorMode.STRICT,
123
+ buildCommand: 'npm run build',
124
+ testCommand: 'npm test',
125
+ };
126
+ const templateContent = 'NestJS Content';
127
+ mockTemplateProvider.getTemplate.mockResolvedValue(templateContent);
128
+ await pipelineGenerator.generatePipeline(projectDetails);
129
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('github-nestjs.yml.ejs'));
130
+ });
131
+ it('should generate a .NET pipeline workflow', async () => {
132
+ const projectDetails = {
133
+ rootPath: '/test/root',
134
+ workflowDirectory: '.github/workflows',
135
+ rulesDirectory: '.agent/rules',
136
+ techStack: { frontend: types_1.FrontendFramework.NONE, backend: types_1.BackendFramework.ASPNET_CORE },
137
+ architecture: types_1.ArchitectureType.MVC,
138
+ rigor: types_1.RigorMode.STRICT,
139
+ buildCommand: 'dotnet build',
140
+ testCommand: 'dotnet test',
141
+ };
142
+ mockTemplateProvider.getTemplate.mockResolvedValue('content');
143
+ await pipelineGenerator.generatePipeline(projectDetails);
144
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('github-dotnet.yml.ejs'));
145
+ });
146
+ it('should generate a Python pipeline workflow', async () => {
147
+ const projectDetails = {
148
+ rootPath: '/test/root',
149
+ workflowDirectory: '.github/workflows',
150
+ rulesDirectory: '.agent/rules',
151
+ techStack: { frontend: types_1.FrontendFramework.NONE, backend: types_1.BackendFramework.PYTHON },
152
+ architecture: types_1.ArchitectureType.MVC,
153
+ rigor: types_1.RigorMode.STRICT,
154
+ buildCommand: 'python build',
155
+ testCommand: 'python test',
156
+ };
157
+ mockTemplateProvider.getTemplate.mockResolvedValue('content');
158
+ await pipelineGenerator.generatePipeline(projectDetails);
159
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('github-python.yml.ejs'));
160
+ });
161
+ it('should generate a Rust pipeline workflow', async () => {
162
+ const projectDetails = {
163
+ rootPath: '/test/root',
164
+ workflowDirectory: '.github/workflows',
165
+ rulesDirectory: '.agent/rules',
166
+ techStack: { frontend: types_1.FrontendFramework.NONE, backend: types_1.BackendFramework.RUST },
167
+ architecture: types_1.ArchitectureType.MVC,
168
+ rigor: types_1.RigorMode.STRICT,
169
+ buildCommand: 'cargo build',
170
+ testCommand: 'cargo test',
171
+ };
172
+ mockTemplateProvider.getTemplate.mockResolvedValue('Rust Content');
173
+ await pipelineGenerator.generatePipeline(projectDetails);
174
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('github-rust.yml.ejs'));
175
+ });
176
+ it('should generate a default Node pipeline workflow', async () => {
177
+ const projectDetails = {
178
+ rootPath: '/test/root',
179
+ workflowDirectory: '.github/workflows',
180
+ rulesDirectory: '.agent/rules',
181
+ techStack: { frontend: types_1.FrontendFramework.NONE, backend: types_1.BackendFramework.NODE },
182
+ architecture: types_1.ArchitectureType.MVC,
183
+ rigor: types_1.RigorMode.STRICT,
184
+ buildCommand: 'npm run build',
185
+ testCommand: 'npm test',
186
+ };
187
+ mockTemplateProvider.getTemplate.mockResolvedValue('Node Content');
188
+ await pipelineGenerator.generatePipeline(projectDetails);
189
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('github-node.yml.ejs'));
190
+ });
191
+ it('should log error if generation fails', async () => {
192
+ const projectDetails = {
193
+ rootPath: '/test/root',
194
+ workflowDirectory: '.github/workflows',
195
+ rulesDirectory: '.agent/rules',
196
+ techStack: { frontend: types_1.FrontendFramework.REACT, backend: types_1.BackendFramework.NONE },
197
+ architecture: types_1.ArchitectureType.MVC,
198
+ rigor: types_1.RigorMode.STRICT,
199
+ buildCommand: 'npm run build',
200
+ testCommand: 'npm run test',
201
+ };
202
+ mockTemplateProvider.getTemplate.mockRejectedValue(new Error('Template Fail'));
203
+ await pipelineGenerator.generatePipeline(projectDetails);
204
+ expect(mockLogger.error).toHaveBeenCalledWith(expect.stringContaining('Failed to generate pipeline'));
205
+ });
206
+ });
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const RulesComposer_1 = require("../../src/core/RulesComposer");
4
+ const types_1 = require("../../src/core/types");
5
+ describe('RulesComposer', () => {
6
+ let rulesComposer;
7
+ let mockTemplateProvider;
8
+ let mockLocalizationService;
9
+ beforeEach(() => {
10
+ mockTemplateProvider = {
11
+ getTemplate: jest.fn(),
12
+ render: jest.fn(),
13
+ };
14
+ mockLocalizationService = {
15
+ setLanguage: jest.fn(),
16
+ getLanguage: jest.fn().mockReturnValue('en'),
17
+ translate: jest.fn(),
18
+ };
19
+ rulesComposer = new RulesComposer_1.RulesComposer(mockTemplateProvider, mockLocalizationService);
20
+ });
21
+ it('should compose rules with base, rigor and framework specific fragments', async () => {
22
+ const stack = {
23
+ frontend: types_1.FrontendFramework.REACT,
24
+ backend: types_1.BackendFramework.NESTJS,
25
+ };
26
+ const rigor = types_1.RigorMode.STRICT;
27
+ const baseTemplate = 'Base Rules\n';
28
+ const rigorTemplate = 'Strict Rules\n';
29
+ const reactTemplate = 'React Rules\n';
30
+ const nestTemplate = 'NestJS Rules\n';
31
+ mockTemplateProvider.getTemplate.mockImplementation(async (path) => {
32
+ if (path.includes('base-rules.md.ejs'))
33
+ return baseTemplate;
34
+ if (path.includes('strict.md.ejs'))
35
+ return rigorTemplate;
36
+ if (path.includes('react.md.ejs'))
37
+ return reactTemplate;
38
+ if (path.includes('nestjs.md.ejs'))
39
+ return nestTemplate;
40
+ return '';
41
+ });
42
+ // Mock render to just return the content as is for this test
43
+ mockTemplateProvider.render.mockImplementation((content) => content);
44
+ const result = await rulesComposer.composeRules(stack, rigor);
45
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('base-rules'));
46
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('strict'));
47
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('react'));
48
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('nestjs'));
49
+ expect(result).toContain('Base Rules');
50
+ expect(result).toContain('Strict Rules');
51
+ expect(result).toContain('React Rules');
52
+ expect(result).toContain('NestJS Rules');
53
+ });
54
+ it('should not include framework rules if framework is NONE', async () => {
55
+ const stack = {
56
+ frontend: types_1.FrontendFramework.NONE,
57
+ backend: types_1.BackendFramework.NONE,
58
+ };
59
+ const rigor = types_1.RigorMode.PROTOTYPE;
60
+ mockTemplateProvider.getTemplate.mockResolvedValue('Rule Content');
61
+ mockTemplateProvider.render.mockImplementation((c) => c);
62
+ await rulesComposer.composeRules(stack, rigor);
63
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('base-rules'));
64
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('prototype'));
65
+ expect(mockTemplateProvider.getTemplate).not.toHaveBeenCalledWith(expect.stringContaining('react'));
66
+ expect(mockTemplateProvider.getTemplate).not.toHaveBeenCalledWith(expect.stringContaining('node'));
67
+ });
68
+ it('should include smart contract rules if present', async () => {
69
+ const stack = {
70
+ frontend: types_1.FrontendFramework.NONE,
71
+ backend: types_1.BackendFramework.NONE,
72
+ smartContract: types_1.SmartContractFramework.SCRYPTO,
73
+ };
74
+ const rigor = types_1.RigorMode.STRICT;
75
+ mockTemplateProvider.getTemplate.mockImplementation(async (path) => {
76
+ if (path.includes('base-rules.md.ejs'))
77
+ return 'Base';
78
+ if (path.includes('strict.md.ejs'))
79
+ return 'Strict';
80
+ if (path.includes('scrypto.md.ejs'))
81
+ return 'Scrypto Rules';
82
+ return '';
83
+ });
84
+ mockTemplateProvider.render.mockImplementation((c) => c);
85
+ const result = await rulesComposer.composeRules(stack, rigor);
86
+ expect(mockTemplateProvider.getTemplate).toHaveBeenCalledWith(expect.stringContaining('scrypto.md.ejs'));
87
+ expect(result).toContain('Scrypto Rules');
88
+ });
89
+ });
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const StackDetector_1 = require("../../src/core/StackDetector");
4
+ const types_1 = require("../../src/core/types");
5
+ describe('StackDetector', () => {
6
+ let stackDetector;
7
+ let mockFileSystem;
8
+ let mockLogger;
9
+ beforeEach(() => {
10
+ mockFileSystem = {
11
+ exists: jest.fn(),
12
+ readFile: jest.fn(),
13
+ writeFile: jest.fn(),
14
+ createDirectory: jest.fn(),
15
+ };
16
+ mockLogger = {
17
+ info: jest.fn(),
18
+ warn: jest.fn(),
19
+ success: jest.fn(),
20
+ error: jest.fn(),
21
+ };
22
+ stackDetector = new StackDetector_1.StackDetector(mockFileSystem, mockLogger);
23
+ });
24
+ it('should detect Rust backend', async () => {
25
+ mockFileSystem.exists.mockImplementation(async (p) => p.endsWith('Cargo.toml'));
26
+ mockFileSystem.readFile.mockResolvedValue('name = "test"');
27
+ const result = await stackDetector.detectStack('/root');
28
+ expect(result.backend).toBe(types_1.BackendFramework.RUST);
29
+ });
30
+ it('should detect Scrypto smart contract', async () => {
31
+ mockFileSystem.exists.mockImplementation(async (p) => p.endsWith('Cargo.toml'));
32
+ mockFileSystem.readFile.mockResolvedValue('[dependencies]\nscrypto = "1.0"');
33
+ const result = await stackDetector.detectStack('/root');
34
+ expect(result.smartContract).toBe(types_1.SmartContractFramework.SCRYPTO);
35
+ });
36
+ it('should detect Flutter frontend', async () => {
37
+ mockFileSystem.exists.mockImplementation(async (p) => p.endsWith('pubspec.yaml'));
38
+ const result = await stackDetector.detectStack('/root');
39
+ expect(result.frontend).toBe(types_1.FrontendFramework.FLUTTER);
40
+ });
41
+ it('should detect Python backend', async () => {
42
+ mockFileSystem.exists.mockImplementation(async (p) => p.endsWith('requirements.txt'));
43
+ const result = await stackDetector.detectStack('/root');
44
+ expect(result.backend).toBe(types_1.BackendFramework.PYTHON);
45
+ });
46
+ it('should detect React frontend (package.json)', async () => {
47
+ mockFileSystem.exists.mockImplementation(async (p) => p.endsWith('package.json'));
48
+ mockFileSystem.readFile.mockResolvedValue(JSON.stringify({
49
+ dependencies: { react: '^18.0.0' }
50
+ }));
51
+ const result = await stackDetector.detectStack('/root');
52
+ expect(result.frontend).toBe(types_1.FrontendFramework.REACT);
53
+ expect(result.backend).toBe(types_1.BackendFramework.NODE); // Default fallback
54
+ });
55
+ it('should detect NestJS backend (package.json)', async () => {
56
+ mockFileSystem.exists.mockImplementation(async (p) => p.endsWith('package.json'));
57
+ mockFileSystem.readFile.mockResolvedValue(JSON.stringify({
58
+ dependencies: { '@nestjs/core': '^8.0.0' }
59
+ }));
60
+ const result = await stackDetector.detectStack('/root');
61
+ expect(result.backend).toBe(types_1.BackendFramework.NESTJS);
62
+ });
63
+ it('should handle errors gracefully', async () => {
64
+ mockFileSystem.exists.mockRejectedValue(new Error('FS Error'));
65
+ const result = await stackDetector.detectStack('/root');
66
+ expect(result).toEqual({});
67
+ expect(mockLogger.warn).toHaveBeenCalled();
68
+ });
69
+ });