genaicode 0.0.31 → 0.0.33

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 (37) hide show
  1. package/README.md +2 -10
  2. package/bin/vertex-monkey-patch.cjs +1 -1
  3. package/package.json +6 -3
  4. package/src/ai-service/anthropic.js +6 -3
  5. package/src/ai-service/chat-gpt.js +5 -2
  6. package/src/ai-service/common.test.js +106 -0
  7. package/src/ai-service/dall-e.js +29 -8
  8. package/src/ai-service/function-calling.js +137 -10
  9. package/src/ai-service/vertex-ai-claude.js +5 -2
  10. package/src/ai-service/vertex-ai-imagen.js +78 -0
  11. package/src/ai-service/vertex-ai.js +9 -8
  12. package/src/cli/cli-options.js +14 -0
  13. package/src/cli/cli-options.test.js +25 -0
  14. package/src/cli/cli-params.js +19 -4
  15. package/src/cli/validate-cli-params.js +28 -1
  16. package/src/cli/validate-cli-params.test.js +31 -0
  17. package/src/files/file-utils.js +7 -0
  18. package/src/files/file-utils.test.js +21 -0
  19. package/src/files/find-files.js +4 -66
  20. package/src/files/read-files.js +20 -1
  21. package/src/files/read-files.test.js +102 -0
  22. package/src/files/temp-buffer.js +13 -0
  23. package/src/files/update-files.js +103 -16
  24. package/src/images/ensure-alpha.js +5 -0
  25. package/src/images/imgly-remove-background.js +23 -0
  26. package/src/images/resize-image.js +23 -0
  27. package/src/images/split-image.js +26 -0
  28. package/src/main/codegen.js +10 -3
  29. package/src/main/codegen.test.js +154 -6
  30. package/src/main/config-lib.js +33 -0
  31. package/src/main/config-lib.test.js +80 -0
  32. package/src/main/config.js +39 -0
  33. package/src/prompt/prompt-codegen.js +1 -1
  34. package/src/prompt/prompt-service.js +18 -9
  35. package/src/prompt/prompt-service.test.js +25 -11
  36. package/src/prompt/systemprompt.js +2 -2
  37. package/src/prompt/systemprompt.test.js +5 -2
@@ -0,0 +1,23 @@
1
+ import sharp from 'sharp';
2
+ import fs from 'fs';
3
+
4
+ /**
5
+ *
6
+ * @param {Buffer} input
7
+ * @param {{width: number, height: number}} size
8
+ * @returns {Buffer}
9
+ */
10
+ export async function resizeImageBuffer(input, size) {
11
+ return await sharp(input).resize(size.width, size.height).toBuffer();
12
+ }
13
+
14
+ /**
15
+ *
16
+ * @param {string} filePath
17
+ * @param {{width: number, height: number}} size
18
+ * @returns {Buffer}
19
+ */
20
+ export async function resizeImageFile(filePath, size) {
21
+ const buffer = await resizeImageBuffer(filePath, size);
22
+ fs.writeFileSync(filePath, buffer);
23
+ }
@@ -0,0 +1,26 @@
1
+ import sharp from 'sharp';
2
+
3
+ /**
4
+ * This function opens the input file, and splits its into parts, extracting parts of the input image, and saving to outputFilePaths
5
+ *
6
+ * @param {string} inputFilePath
7
+ * @param {Array<{rect: {x: number, y: number, width: number, height: number}, outputFilePath: string}>} parts
8
+ */
9
+ export async function splitImage(inputFilePath, parts) {
10
+ try {
11
+ // Process each part
12
+ for (const part of parts) {
13
+ const { rect, outputFilePath } = part;
14
+ const { x, y, width, height } = rect;
15
+
16
+ // Extract the specified rectangle from the input image
17
+ console.log('Extracting', rect, outputFilePath);
18
+ await sharp(inputFilePath).extract({ left: x, top: y, width, height }).toFile(outputFilePath);
19
+ }
20
+
21
+ console.log('Image splitting completed successfully.');
22
+ } catch (error) {
23
+ console.error('Error splitting image:', error);
24
+ throw error;
25
+ }
26
+ }
@@ -10,15 +10,19 @@ import {
10
10
  vertexAiClaude,
11
11
  disableInitialLint,
12
12
  helpRequested,
13
+ imagen,
13
14
  } from '../cli/cli-params.js';
14
15
  import { validateCliParams } from '../cli/validate-cli-params.js';
15
16
  import { generateContent as generateContentVertexAi } from '../ai-service/vertex-ai.js';
16
17
  import { generateContent as generateContentGPT } from '../ai-service/chat-gpt.js';
17
18
  import { generateContent as generateContentAnthropic } from '../ai-service/anthropic.js';
18
19
  import { generateContent as generateContentVertexAiClaude } from '../ai-service/vertex-ai-claude.js';
20
+ import { generateImage as generateImageDallE } from '../ai-service/dall-e.js';
21
+ import { generateImage as generateImageVertexAi } from '../ai-service/vertex-ai-imagen.js';
22
+
19
23
  import { promptService } from '../prompt/prompt-service.js';
20
24
  import { updateFiles } from '../files/update-files.js';
21
- import { rcConfig } from '../files/find-files.js';
25
+ import { rcConfig } from '../main/config.js';
22
26
  import { getLintFixPrompt } from '../prompt/prompt-codegen.js';
23
27
  import { printHelpMessage } from '../cli/cli-options.js';
24
28
 
@@ -63,8 +67,11 @@ export async function runCodegen() {
63
67
  ? generateContentGPT
64
68
  : assert(false, 'Please specify which AI service should be used');
65
69
 
70
+ const generateImage =
71
+ imagen === 'vertex-ai' ? generateImageVertexAi : imagen === 'dall-e' ? generateImageDallE : undefined;
72
+
66
73
  console.log('Generating response');
67
- let functionCalls = await promptService(generateContent);
74
+ let functionCalls = await promptService(generateContent, generateImage);
68
75
  console.log('Received function calls:', functionCalls);
69
76
 
70
77
  if (dryRun) {
@@ -87,7 +94,7 @@ export async function runCodegen() {
87
94
  const lintErrorPrompt = getLintFixPrompt(rcConfig.lintCommand, error.stdout, error.stderr);
88
95
 
89
96
  console.log('Generating response for lint fixes');
90
- const lintFixFunctionCalls = await promptService(generateContent, lintErrorPrompt);
97
+ const lintFixFunctionCalls = await promptService(generateContent, generateImage, lintErrorPrompt);
91
98
 
92
99
  console.log('Received function calls for lint fixes:', lintFixFunctionCalls);
93
100
 
@@ -10,6 +10,9 @@ import * as updateFiles from '../files/update-files.js';
10
10
  import '../files/find-files.js';
11
11
  import * as cliParams from '../cli/cli-params.js';
12
12
  import * as cliOptions from '../cli/cli-options.js';
13
+ import * as vertexAiImagen from '../ai-service/vertex-ai-imagen.js';
14
+ import * as dallE from '../ai-service/dall-e.js';
15
+ import './config.js';
13
16
 
14
17
  vi.mock('../ai-service/vertex-ai.js', () => ({ generateContent: vi.fn() }));
15
18
  vi.mock('../ai-service/chat-gpt.js', () => ({ generateContent: vi.fn() }));
@@ -31,19 +34,26 @@ vi.mock('../cli/cli-params.js', () => ({
31
34
  vision: false,
32
35
  imagen: false,
33
36
  temperature: 0.7,
37
+ cheap: false,
38
+ taskFile: undefined,
39
+ disableInitialLint: undefined,
34
40
  }));
35
41
  vi.mock('../files/find-files.js', () => ({
36
- rootDir: '/mocked/root/dir',
37
- rcConfig: {
38
- rootDir: '.',
39
- extensions: ['.js', '.ts', '.tsx', '.jsx'],
40
- },
41
42
  getSourceFiles: () => [],
42
43
  getImageAssetFiles: () => [],
43
44
  }));
44
45
  vi.mock('../cli/cli-options.js', () => ({
45
46
  printHelpMessage: vi.fn(),
46
47
  }));
48
+ vi.mock('../ai-service/vertex-ai-imagen.js', () => ({ generateImage: vi.fn() }));
49
+ vi.mock('../ai-service/dall-e.js', () => ({ generateImage: vi.fn() }));
50
+ vi.mock('./config.js', () => ({
51
+ rootDir: '/mocked/root/dir',
52
+ rcConfig: {
53
+ rootDir: '.',
54
+ extensions: ['.js', '.ts', '.tsx', '.jsx'],
55
+ },
56
+ }));
47
57
 
48
58
  describe('runCodegen', () => {
49
59
  beforeEach(() => {
@@ -55,6 +65,7 @@ describe('runCodegen', () => {
55
65
  cliParams.dryRun = false;
56
66
  cliParams.helpRequested = false;
57
67
  cliParams.vision = false;
68
+ cliParams.imagen = false;
58
69
  });
59
70
 
60
71
  it('should run codegen with Vertex AI by default', async () => {
@@ -135,7 +146,13 @@ describe('runCodegen', () => {
135
146
 
136
147
  await runCodegen();
137
148
 
138
- expect(vertexAi.generateContent).toHaveBeenCalledWith(expect.anything(), expect.anything(), expect.anything(), 0.5);
149
+ expect(vertexAi.generateContent).toHaveBeenCalledWith(
150
+ expect.anything(),
151
+ expect.anything(),
152
+ expect.anything(),
153
+ 0.5,
154
+ false,
155
+ );
139
156
  expect(updateFiles.updateFiles).toHaveBeenCalledWith(mockFunctionCalls);
140
157
  });
141
158
 
@@ -183,4 +200,135 @@ describe('runCodegen', () => {
183
200
  ]),
184
201
  );
185
202
  });
203
+
204
+ it('should use Vertex AI Imagen when imagen flag is set to vertex-ai', async () => {
205
+ cliParams.imagen = 'vertex-ai';
206
+ cliParams.vertexAi = true;
207
+
208
+ const mockCodegenSummary = [
209
+ {
210
+ name: 'codegenSummary',
211
+ args: {
212
+ files: [{ path: 'test.js', updateToolName: 'updateFile' }],
213
+ contextPaths: [],
214
+ explanation: 'Mock summary with image generation failure',
215
+ },
216
+ },
217
+ ];
218
+ const mockFunctionCalls = [
219
+ { name: 'generateImage', args: { prompt: 'A beautiful landscape', filePath: 'landscape.png', size: '512x512' } },
220
+ ];
221
+ vertexAi.generateContent.mockResolvedValueOnce(mockCodegenSummary);
222
+ vertexAi.generateContent.mockResolvedValueOnce(mockFunctionCalls);
223
+ vertexAiImagen.generateImage.mockResolvedValueOnce('mocked-image-data');
224
+
225
+ await runCodegen();
226
+
227
+ expect(vertexAi.generateContent).toHaveBeenCalled();
228
+ expect(vertexAiImagen.generateImage).toHaveBeenCalledWith('A beautiful landscape', undefined, '512x512', false);
229
+ expect(updateFiles.updateFiles).toHaveBeenCalledWith(mockFunctionCalls);
230
+ });
231
+
232
+ it('should use DALL-E when imagen flag is set to dall-e', async () => {
233
+ cliParams.imagen = 'dall-e';
234
+ cliParams.chatGpt = true;
235
+
236
+ const mockCodegenSummary = [
237
+ {
238
+ name: 'codegenSummary',
239
+ args: {
240
+ files: [{ path: 'test.js', updateToolName: 'updateFile' }],
241
+ contextPaths: [],
242
+ explanation: 'Mock summary with image generation failure',
243
+ },
244
+ },
245
+ ];
246
+ const mockFunctionCalls = [
247
+ { name: 'generateImage', args: { prompt: 'A futuristic city', filePath: 'city.png', size: '1024x1024' } },
248
+ ];
249
+ chatGpt.generateContent.mockResolvedValueOnce(mockCodegenSummary);
250
+ chatGpt.generateContent.mockResolvedValueOnce(mockFunctionCalls);
251
+ dallE.generateImage.mockResolvedValueOnce('mocked-image-data');
252
+
253
+ await runCodegen();
254
+
255
+ expect(chatGpt.generateContent).toHaveBeenCalled();
256
+ expect(dallE.generateImage).toHaveBeenCalledWith('A futuristic city', undefined, '1024x1024', false);
257
+ expect(updateFiles.updateFiles).toHaveBeenCalledWith(mockFunctionCalls);
258
+ });
259
+
260
+ it('should throw an error when imagen flag is set but no AI service is specified', async () => {
261
+ cliParams.imagen = 'vertex-ai';
262
+
263
+ await expect(runCodegen()).rejects.toThrow('Please specify which AI service should be used');
264
+ });
265
+
266
+ it('should pass the cheap parameter to the AI service when --cheap flag is true', async () => {
267
+ cliParams.vertexAi = true;
268
+ cliParams.cheap = true;
269
+
270
+ const mockFunctionCalls = [
271
+ { name: 'updateFile', args: { filePath: 'test.js', newContent: 'console.log("Cheap test");' } },
272
+ ];
273
+ vertexAi.generateContent.mockResolvedValueOnce(mockFunctionCalls);
274
+
275
+ await runCodegen();
276
+
277
+ expect(vertexAi.generateContent).toHaveBeenCalledWith(
278
+ expect.anything(),
279
+ expect.anything(),
280
+ expect.anything(),
281
+ expect.anything(),
282
+ true,
283
+ );
284
+ expect(updateFiles.updateFiles).toHaveBeenCalledWith(mockFunctionCalls);
285
+ });
286
+
287
+ it('should pass the cheap parameter to the image generation service when --cheap flag is true', async () => {
288
+ cliParams.imagen = 'vertex-ai';
289
+ cliParams.vertexAi = true;
290
+ cliParams.cheap = true;
291
+
292
+ const mockCodegenSummary = [
293
+ {
294
+ name: 'codegenSummary',
295
+ args: {
296
+ files: [{ path: 'test.js', updateToolName: 'updateFile' }],
297
+ contextPaths: [],
298
+ explanation: 'Mock summary with cheap image generation',
299
+ },
300
+ },
301
+ ];
302
+ const mockFunctionCalls = [
303
+ {
304
+ name: 'generateImage',
305
+ args: {
306
+ prompt: 'A simple landscape',
307
+ filePath: 'landscape.png',
308
+ size: { width: 256, height: 256 },
309
+ cheap: true,
310
+ },
311
+ },
312
+ ];
313
+ vertexAi.generateContent.mockResolvedValueOnce(mockCodegenSummary);
314
+ vertexAi.generateContent.mockResolvedValueOnce(mockFunctionCalls);
315
+ vertexAiImagen.generateImage.mockResolvedValueOnce('mocked-cheap-image-data');
316
+
317
+ await runCodegen();
318
+
319
+ expect(vertexAi.generateContent).toHaveBeenCalledWith(
320
+ expect.anything(),
321
+ expect.anything(),
322
+ expect.anything(),
323
+ expect.anything(),
324
+ true,
325
+ );
326
+ expect(vertexAiImagen.generateImage).toHaveBeenCalledWith(
327
+ 'A simple landscape',
328
+ undefined,
329
+ { width: 256, height: 256 },
330
+ true,
331
+ );
332
+ expect(updateFiles.updateFiles).toHaveBeenCalledWith(mockFunctionCalls);
333
+ });
186
334
  });
@@ -0,0 +1,33 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import assert from 'node:assert';
4
+
5
+ import { isAncestorDirectory } from '../files/file-utils.js';
6
+
7
+ // This file contains project codegen configuration
8
+ const CODEGENRC_FILENAME = '.genaicoderc';
9
+
10
+ // Find .genaicoderc file
11
+ export function findRcFile() {
12
+ let rcFilePath = process.cwd();
13
+ while (!fs.existsSync(path.join(rcFilePath, CODEGENRC_FILENAME))) {
14
+ const parentDir = path.dirname(rcFilePath);
15
+ if (parentDir === rcFilePath) {
16
+ throw new Error(`${CODEGENRC_FILENAME} not found in any parent directory`);
17
+ }
18
+ rcFilePath = parentDir;
19
+ }
20
+ return path.join(rcFilePath, CODEGENRC_FILENAME);
21
+ }
22
+
23
+ // Read and parse .genaicoderc file
24
+ export function parseRcFile(rcFilePath) {
25
+ assert(fs.existsSync(rcFilePath), `${CODEGENRC_FILENAME} not found`);
26
+ const rcConfig = JSON.parse(fs.readFileSync(rcFilePath, 'utf-8'));
27
+ assert(rcConfig.rootDir, 'Root dir not configured');
28
+
29
+ const rootDir = path.resolve(path.dirname(rcFilePath), rcConfig.rootDir);
30
+ assert(isAncestorDirectory(path.dirname(rcFilePath), rootDir), 'Root dir is not located inside project directory');
31
+
32
+ return { ...rcConfig, rootDir };
33
+ }
@@ -0,0 +1,80 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { isAncestorDirectory } from '../files/file-utils.js';
5
+ import { findRcFile, parseRcFile } from './config-lib';
6
+
7
+ vi.mock('fs');
8
+ vi.mock('path');
9
+ vi.mock('../files/file-utils.js');
10
+
11
+ const CODEGENRC_FILENAME = '.genaicoderc';
12
+
13
+ // Mock data
14
+ const mockRcContent = JSON.stringify({ rootDir: 'src' });
15
+ const mockRootDir = '/project/src';
16
+ const mockRcFilePath = `/project/${CODEGENRC_FILENAME}`;
17
+
18
+ // Helper function to mock fs.existsSync
19
+ function mockExistsSync(paths) {
20
+ return (p) => paths.includes(p);
21
+ }
22
+
23
+ // Tests
24
+
25
+ // Test for findRcFile
26
+ describe('findRcFile', () => {
27
+ it('should find .genaicoderc in the current or parent directories', () => {
28
+ fs.existsSync.mockImplementation(mockExistsSync([mockRcFilePath]));
29
+ path.dirname.mockImplementation((p) => (p === '/project' ? '/' : '/project'));
30
+ path.join.mockImplementation((...args) => args.join('/'));
31
+
32
+ const result = findRcFile();
33
+ expect(result).toBe(mockRcFilePath);
34
+ });
35
+
36
+ it('should throw an error if .genaicoderc is not found', () => {
37
+ fs.existsSync.mockReturnValue(false);
38
+ path.dirname.mockImplementation((p) => (p === '/' ? '/' : '/project'));
39
+
40
+ expect(() => findRcFile()).toThrowError(`${CODEGENRC_FILENAME} not found in any parent directory`);
41
+ });
42
+ });
43
+
44
+ // Test for parseRcFile
45
+ describe('parseRcFile', () => {
46
+ it('should parse .genaicoderc and return config with rootDir', () => {
47
+ fs.existsSync.mockReturnValue(true);
48
+ fs.readFileSync.mockReturnValue(mockRcContent);
49
+ path.resolve.mockImplementation((...args) => args.join('/'));
50
+ path.dirname.mockReturnValue('/project');
51
+ isAncestorDirectory.mockReturnValue(true);
52
+
53
+ const result = parseRcFile(mockRcFilePath);
54
+ expect(result).toEqual({ ...JSON.parse(mockRcContent), rootDir: mockRootDir });
55
+ });
56
+
57
+ it('should throw an error if .genaicoderc is missing', () => {
58
+ fs.existsSync.mockReturnValue(false);
59
+
60
+ expect(() => parseRcFile(mockRcFilePath)).toThrowError(`${CODEGENRC_FILENAME} not found`);
61
+ });
62
+
63
+ it('should throw an error if rootDir is not configured', () => {
64
+ const invalidContent = JSON.stringify({});
65
+ fs.existsSync.mockReturnValue(true);
66
+ fs.readFileSync.mockReturnValue(invalidContent);
67
+
68
+ expect(() => parseRcFile(mockRcFilePath)).toThrowError('Root dir not configured');
69
+ });
70
+
71
+ it('should throw an error if rootDir is not located inside project directory', () => {
72
+ fs.existsSync.mockReturnValue(true);
73
+ fs.readFileSync.mockReturnValue(mockRcContent);
74
+ path.resolve.mockImplementation((...args) => args.join('/'));
75
+ path.dirname.mockReturnValue('/project');
76
+ isAncestorDirectory.mockReturnValue(false);
77
+
78
+ expect(() => parseRcFile(mockRcFilePath)).toThrowError('Root dir is not located inside project directory');
79
+ });
80
+ });
@@ -0,0 +1,39 @@
1
+ import { findRcFile, parseRcFile } from './config-lib.js';
2
+
3
+ // Default extensions if not specified in .genaicoderc
4
+ const DEFAULT_EXTENSIONS = [
5
+ '.md',
6
+ '.js',
7
+ '.ts',
8
+ '.tsx',
9
+ '.css',
10
+ '.scss',
11
+ '.py',
12
+ '.go',
13
+ '.c',
14
+ '.h',
15
+ '.cpp',
16
+ '.txt',
17
+ '.html',
18
+ '.txt',
19
+ '.json',
20
+ ];
21
+
22
+ // A list of paths that are ignored by default
23
+ const DEFAULT_IGNORE_PATHS = ['node_modules', 'build', 'dist', 'package-lock.json', 'coverage'];
24
+
25
+ // Read and parse the configuration
26
+ const rcFilePath = findRcFile();
27
+ export const rcConfig = parseRcFile(rcFilePath);
28
+
29
+ // Use extensions from .genaicoderc if available, otherwise use default
30
+ export const sourceExtensions = rcConfig.extensions || DEFAULT_EXTENSIONS;
31
+
32
+ // Image extensions (driven by ai service limitations)
33
+ export const IMAGE_ASSET_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.webp'];
34
+
35
+ // Export ignore paths
36
+ export const ignorePaths = rcConfig.ignorePaths ?? DEFAULT_IGNORE_PATHS;
37
+
38
+ console.log('Detected codegen configuration', rcConfig);
39
+ console.log('Root dir:', rcConfig.rootDir);
@@ -23,7 +23,7 @@ export function getCodeGenPrompt() {
23
23
  codeGenFiles = Object.keys(getSourceCode());
24
24
  } else {
25
25
  codeGenFiles = Object.entries(getSourceCode())
26
- .filter(([, content]) => content.match(new RegExp("([^'^`]+)" + CODEGEN_TRIGGER)))
26
+ .filter(([, { content }]) => content?.match(new RegExp("([^'^`]+)" + CODEGEN_TRIGGER)))
27
27
  .map(([path]) => path);
28
28
  }
29
29
 
@@ -7,11 +7,10 @@ import { getSystemPrompt } from './systemprompt.js';
7
7
  import { getCodeGenPrompt } from './prompt-codegen.js';
8
8
  import { functionDefs } from '../ai-service/function-calling.js';
9
9
  import { getSourceCode, getImageAssets } from '../files/read-files.js';
10
- import { disableContextOptimization, temperature, vision, imagen } from '../cli/cli-params.js';
11
- import { generateImage } from '../ai-service/dall-e.js';
10
+ import { disableContextOptimization, temperature, vision, cheap } from '../cli/cli-params.js';
12
11
 
13
12
  /** A function that communicates with model using */
14
- export async function promptService(generateContentFn, codegenPrompt = getCodeGenPrompt()) {
13
+ export async function promptService(generateContentFn, generateImageFn, codegenPrompt = getCodeGenPrompt()) {
15
14
  const messages = prepareMessages(codegenPrompt);
16
15
 
17
16
  // First stage: generate code generation summary, which should not take a lot of output tokens
@@ -44,7 +43,7 @@ export async function promptService(generateContentFn, codegenPrompt = getCodeGe
44
43
 
45
44
  prompt.slice(-1)[0].text = messages.prompt;
46
45
 
47
- let baseResult = await generateContentFn(prompt, functionDefs, 'codegenSummary', temperature);
46
+ let baseResult = await generateContentFn(prompt, functionDefs, 'codegenSummary', temperature, cheap);
48
47
 
49
48
  const codegenSummaryRequest = baseResult.find((call) => call.name === 'codegenSummary');
50
49
 
@@ -82,6 +81,7 @@ export async function promptService(generateContentFn, codegenPrompt = getCodeGe
82
81
  console.log('Collecting partial update for: ' + file.path + ' using tool: ' + file.updateToolName);
83
82
  console.log('- Prompt:', file.prompt);
84
83
  console.log('- Temperature', file.temperature);
84
+ console.log('- Cheap', file.cheap);
85
85
  if (vision) {
86
86
  console.log('- Context image assets', file.contextImageAssets);
87
87
  }
@@ -106,6 +106,7 @@ export async function promptService(generateContentFn, codegenPrompt = getCodeGe
106
106
  functionDefs,
107
107
  file.updateToolName,
108
108
  file.temperature ?? temperature,
109
+ file.cheap === true,
109
110
  );
110
111
 
111
112
  let getSourceCodeCall = partialResult.find((call) => call.name === 'getSourceCode');
@@ -114,12 +115,12 @@ export async function promptService(generateContentFn, codegenPrompt = getCodeGe
114
115
  // Handle image generation requests
115
116
  const generateImageCall = partialResult.find((call) => call.name === 'generateImage');
116
117
  if (generateImageCall) {
117
- assert(imagen, 'Image generation requested, but --imagen option not provided');
118
+ assert(!!generateImageFn, 'Image generation requested, but a image generation service was not provided');
118
119
 
119
120
  console.log('Processing image generation request:', generateImageCall.args);
120
121
  try {
121
- const { prompt: imagePrompt, filePath, size } = generateImageCall.args;
122
- const generatedImageUrl = await generateImage(imagePrompt, size);
122
+ const { prompt: imagePrompt, filePath, contextImagePath, size, cheap } = generateImageCall.args;
123
+ const generatedImageUrl = await generateImageFn(imagePrompt, contextImagePath, size, cheap === true);
123
124
 
124
125
  // Add a createFile call to the result to ensure the generated image is tracked
125
126
  partialResult.push({
@@ -161,7 +162,13 @@ export async function promptService(generateContentFn, codegenPrompt = getCodeGe
161
162
  console.log(`Patch could not be applied for ${filePath}. Retrying without patchFile function.`);
162
163
 
163
164
  // Rerun content generation without patchFile function
164
- partialResult = await generateContentFn(prompt, functionDefs, 'updateFile', temperature);
165
+ partialResult = await generateContentFn(
166
+ prompt,
167
+ functionDefs,
168
+ 'updateFile',
169
+ file.temperature,
170
+ file.cheap === true,
171
+ );
165
172
 
166
173
  let getSourceCodeCall = partialResult.find((call) => call.name === 'getSourceCode');
167
174
  assert(!getSourceCodeCall, 'Unexpected getSourceCode: ' + JSON.stringify(getSourceCodeCall));
@@ -200,7 +207,9 @@ function prepareMessages(prompt) {
200
207
  requestSourceCode: 'Please provide application source code.',
201
208
  suggestImageAssets: 'I should also provide you with a summary of application image assets',
202
209
  requestImageAssets: 'Please provide summary of application image assets.',
203
- prompt: prompt + '\n Start from generating codegen summary, this summary will be used to generate updates.',
210
+ prompt:
211
+ prompt +
212
+ '\n Start from generating codegen summary, this summary will be used as a context to generate updates, so make sure that it contains useful information.',
204
213
  sourceCode: JSON.stringify(getSourceCode()),
205
214
  contextSourceCode: (paths) => JSON.stringify(getSourceCode(paths)),
206
215
  imageAssets: JSON.stringify(getImageAssets()),
@@ -30,6 +30,7 @@ vi.mock('../cli/cli-params.js', () => ({
30
30
  vision: false,
31
31
  imagen: false,
32
32
  temperature: 0.7,
33
+ cheap: false,
33
34
  }));
34
35
  vi.mock('fs');
35
36
  vi.mock('diff');
@@ -38,11 +39,6 @@ vi.mock('../ai-service/dall-e.js', () => ({ generateImage: vi.fn() }));
38
39
 
39
40
  // Mock find-files module
40
41
  vi.mock('../files/find-files.js', () => ({
41
- rootDir: '/mocked/root/dir',
42
- rcConfig: {
43
- rootDir: '.',
44
- extensions: ['.js', '.ts', '.tsx', '.jsx'],
45
- },
46
42
  getSourceFiles: () => [],
47
43
  getImageAssetFiles: () => [],
48
44
  }));
@@ -53,6 +49,14 @@ vi.mock('../files/read-files.js', () => ({
53
49
  getImageAssets: vi.fn(() => ({})),
54
50
  }));
55
51
 
52
+ vi.mock('../main/config.js', () => ({
53
+ rootDir: '/mocked/root/dir',
54
+ rcConfig: {
55
+ rootDir: '.',
56
+ extensions: ['.js', '.ts', '.tsx', '.jsx'],
57
+ },
58
+ }));
59
+
56
60
  describe('promptService', () => {
57
61
  beforeEach(() => {
58
62
  vi.resetAllMocks();
@@ -394,7 +398,7 @@ describe('promptService', () => {
394
398
  args: {
395
399
  prompt: 'A test image',
396
400
  filePath: '/path/to/generated/image.png',
397
- size: '256x256',
401
+ size: { width: 256, height: 256 },
398
402
  },
399
403
  },
400
404
  ];
@@ -413,10 +417,15 @@ describe('promptService', () => {
413
417
  vertexAi.generateContent.mockResolvedValueOnce(mockGenerateImageCall);
414
418
  dalleService.generateImage.mockResolvedValue('https://example.com/generated-image.png');
415
419
 
416
- const result = await promptService(vertexAi.generateContent);
420
+ const result = await promptService(vertexAi.generateContent, dalleService.generateImage);
417
421
 
418
422
  expect(vertexAi.generateContent).toHaveBeenCalledTimes(2);
419
- expect(dalleService.generateImage).toHaveBeenCalledWith('A test image', '256x256');
423
+ expect(dalleService.generateImage).toHaveBeenCalledWith(
424
+ 'A test image',
425
+ undefined,
426
+ { width: 256, height: 256 },
427
+ false,
428
+ );
420
429
  expect(result).toEqual(expect.arrayContaining(mockDownloadFileCall));
421
430
  });
422
431
 
@@ -439,7 +448,7 @@ describe('promptService', () => {
439
448
  args: {
440
449
  prompt: 'A test image',
441
450
  filePath: '/path/to/generated/image.png',
442
- size: '256x256',
451
+ size: { width: 256, height: 256 },
443
452
  },
444
453
  },
445
454
  ];
@@ -448,10 +457,15 @@ describe('promptService', () => {
448
457
  vertexAi.generateContent.mockResolvedValueOnce(mockGenerateImageCall);
449
458
  dalleService.generateImage.mockRejectedValue(new Error('Image generation failed'));
450
459
 
451
- const result = await promptService(vertexAi.generateContent);
460
+ const result = await promptService(vertexAi.generateContent, dalleService.generateImage);
452
461
 
453
462
  expect(vertexAi.generateContent).toHaveBeenCalledTimes(2);
454
- expect(dalleService.generateImage).toHaveBeenCalledWith('A test image', '256x256');
463
+ expect(dalleService.generateImage).toHaveBeenCalledWith(
464
+ 'A test image',
465
+ undefined,
466
+ { width: 256, height: 256 },
467
+ false,
468
+ );
455
469
  expect(result).toEqual(
456
470
  expect.arrayContaining([
457
471
  expect.objectContaining({
@@ -1,7 +1,7 @@
1
1
  import { CODEGEN_TRIGGER } from './prompt-consts.js';
2
2
  import { verbosePrompt } from '../cli/cli-params.js';
3
3
  import { verifySystemPromptLimit } from './limits.js';
4
- import { rootDir } from '../files/find-files.js';
4
+ import { rcConfig } from '../main/config.js';
5
5
 
6
6
  /** Generates a system prompt */
7
7
  export function getSystemPrompt() {
@@ -16,7 +16,7 @@ export function getSystemPrompt() {
16
16
 
17
17
  You should parse my application source code and then suggest changes using appropriate tools.
18
18
 
19
- The root directory of my application is \`${rootDir}\` and you should limit the changes only to this path.
19
+ The root directory of my application is \`${rcConfig.rootDir}\` and you should limit the changes only to this path.
20
20
 
21
21
  When suggesting changes always use absolute file paths.
22
22
  `;
@@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
2
  import { getSystemPrompt } from './systemprompt.js';
3
3
  import * as cliParams from '../cli/cli-params.js';
4
4
  import '../files/find-files.js';
5
+ import '../main/config.js';
5
6
 
6
7
  vi.mock('../cli/cli-params.js', () => ({
7
8
  requireExplanations: false,
@@ -16,11 +17,13 @@ vi.mock('../cli/cli-params.js', () => ({
16
17
  }));
17
18
 
18
19
  vi.mock('../files/find-files.js', () => ({
19
- rcConfig: {},
20
- rootDir: '/mocked/root/dir',
21
20
  getSourceFiles: vi.fn(),
22
21
  }));
23
22
 
23
+ vi.mock('../main/config.js', () => ({
24
+ rcConfig: { rootDir: '/mocked/root/dir' },
25
+ }));
26
+
24
27
  describe('getSystemPrompt', () => {
25
28
  beforeEach(() => {
26
29
  vi.clearAllMocks();