genaicode 0.0.31

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 (38) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +130 -0
  3. package/bin/genaicode.cjs +8 -0
  4. package/bin/genaicode.js +5 -0
  5. package/bin/vertex-monkey-patch.cjs +33 -0
  6. package/media/demo-for-readme.gif +0 -0
  7. package/media/logo-dark.png +0 -0
  8. package/media/logo.png +0 -0
  9. package/package.json +58 -0
  10. package/src/ai-service/anthropic.js +91 -0
  11. package/src/ai-service/chat-gpt.js +96 -0
  12. package/src/ai-service/common.js +40 -0
  13. package/src/ai-service/dall-e.js +29 -0
  14. package/src/ai-service/function-calling.js +320 -0
  15. package/src/ai-service/vertex-ai-claude.js +95 -0
  16. package/src/ai-service/vertex-ai.js +155 -0
  17. package/src/cli/cli-options.js +106 -0
  18. package/src/cli/cli-options.test.js +25 -0
  19. package/src/cli/cli-params.js +84 -0
  20. package/src/cli/cli-params.test.js +43 -0
  21. package/src/cli/service-autodetect.js +11 -0
  22. package/src/cli/service-autodetect.test.js +43 -0
  23. package/src/cli/validate-cli-params.js +90 -0
  24. package/src/cli/validate-cli-params.test.js +103 -0
  25. package/src/files/find-files.js +148 -0
  26. package/src/files/read-files.js +41 -0
  27. package/src/files/update-files.js +118 -0
  28. package/src/main/codegen.js +113 -0
  29. package/src/main/codegen.test.js +186 -0
  30. package/src/prompt/limits.js +23 -0
  31. package/src/prompt/limits.test.js +40 -0
  32. package/src/prompt/prompt-codegen.js +106 -0
  33. package/src/prompt/prompt-codegen.test.js +116 -0
  34. package/src/prompt/prompt-consts.js +1 -0
  35. package/src/prompt/prompt-service.js +211 -0
  36. package/src/prompt/prompt-service.test.js +486 -0
  37. package/src/prompt/systemprompt.js +32 -0
  38. package/src/prompt/systemprompt.test.js +60 -0
@@ -0,0 +1,118 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import assert from 'node:assert';
4
+ import * as diff from 'diff';
5
+
6
+ import { isAncestorDirectory, getSourceFiles, rootDir } from './find-files.js';
7
+ import {
8
+ allowDirectoryCreate,
9
+ allowFileCreate,
10
+ allowFileDelete,
11
+ allowFileMove,
12
+ anthropic,
13
+ chatGpt,
14
+ vertexAiClaude,
15
+ } from '../cli/cli-params.js';
16
+
17
+ /**
18
+ * @param functionCalls Result of the code generation, a map of file paths to new content
19
+ */
20
+ export async function updateFiles(functionCalls) {
21
+ for (const { name, args } of functionCalls) {
22
+ let { filePath, newContent, source, destination, patch } = args;
23
+
24
+ // Check if filePath is absolute, if not use rootDir as baseline
25
+ if (name !== 'moveFile') {
26
+ filePath = path.isAbsolute(filePath) ? filePath : path.join(rootDir, filePath);
27
+ } else {
28
+ source = path.isAbsolute(source) ? source : path.join(rootDir, source);
29
+ destination = path.isAbsolute(destination) ? destination : path.join(rootDir, destination);
30
+ }
31
+
32
+ // ignore files which are not located inside project directory (sourceFiles)
33
+ if (
34
+ (name !== 'moveFile' && !isProjectPath(filePath)) ||
35
+ (name === 'moveFile' && (!isProjectPath(source) || !isProjectPath(destination)))
36
+ ) {
37
+ console.log(`Skipping file: ${filePath || source}`);
38
+ throw new Error(`File ${filePath || source} is not located inside project directory, something is wrong?`);
39
+ }
40
+
41
+ if (name === 'deleteFile') {
42
+ assert(allowFileDelete, 'File delete option was not enabled');
43
+ console.log(`Removing file: ${filePath}`);
44
+ fs.unlinkSync(filePath);
45
+ } else if (name === 'createDirectory') {
46
+ assert(allowDirectoryCreate, 'Directory create option was not enabled');
47
+ console.log(`Creating directory: ${filePath}`);
48
+ fs.mkdirSync(filePath, { recursive: true });
49
+ } else if (name === 'updateFile' || name === 'createFile' || name === 'patchFile') {
50
+ if (name === 'patchFile') {
51
+ console.log(`Applying a patch: ${filePath} content`);
52
+ newContent = diff.applyPatch(fs.readFileSync(filePath, 'utf-8'), patch);
53
+ assert(!!newContent, 'Patch was not successful');
54
+ }
55
+
56
+ assert(!!newContent, 'newContent must not be empty');
57
+ if (name === 'createFile') {
58
+ console.log(`Creating file: ${filePath}`);
59
+ assert(allowFileCreate, 'File create option was not enabled');
60
+ assert(!fs.existsSync(filePath), 'File already exists');
61
+ if (allowDirectoryCreate) {
62
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
63
+ }
64
+ } else {
65
+ console.log(`Updating file: ${filePath}`);
66
+ assert(fs.existsSync(filePath), 'File does not exist');
67
+ }
68
+ fs.writeFileSync(
69
+ filePath,
70
+ chatGpt || anthropic || vertexAiClaude
71
+ ? newContent
72
+ : // Fixing a problem caused by vertex function calling. Possibly not a good fix
73
+ newContent.replace(/\\n/g, '\n').replace(/\\'/g, "'"),
74
+ 'utf-8',
75
+ );
76
+ } else if (name === 'moveFile') {
77
+ console.log(`Moving file from ${source} to ${destination}`);
78
+ assert(fs.existsSync(source), 'Source file does not exist');
79
+ assert(!fs.existsSync(destination), 'Destination file already exists');
80
+ assert(allowFileMove, 'File move option was not enabled');
81
+ if (allowDirectoryCreate) {
82
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
83
+ }
84
+ fs.renameSync(source, destination);
85
+ } else if (name === 'downloadFile') {
86
+ console.log(`Downloading image: ${filePath}`);
87
+ assert(fs.existsSync(filePath) || allowFileCreate, 'File create option was not enabled and file does not exist');
88
+ if (allowDirectoryCreate) {
89
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
90
+ }
91
+ try {
92
+ assert(args.downloadUrl, 'image url is not empty');
93
+ const imageResponse = await fetch(args.downloadUrl);
94
+ const arrayBuffer = await imageResponse.arrayBuffer();
95
+ const buffer = Buffer.from(arrayBuffer);
96
+ fs.writeFileSync(filePath, buffer);
97
+ console.log(`Image download and saved to: ${filePath}`);
98
+ } catch (error) {
99
+ console.error(`Failed to download image: ${error.message}`);
100
+ throw error;
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ function isProjectPath(filePath) {
107
+ const sourceFiles = getSourceFiles();
108
+
109
+ return (
110
+ isAncestorDirectory(rootDir, filePath) ||
111
+ sourceFiles.includes(filePath) ||
112
+ !sourceFiles.some(
113
+ (sourceFile) =>
114
+ path.dirname(filePath) === path.dirname(sourceFile) ||
115
+ isAncestorDirectory(path.dirname(sourceFile), path.dirname(filePath)),
116
+ )
117
+ );
118
+ }
@@ -0,0 +1,113 @@
1
+ import assert from 'node:assert';
2
+ import { exec } from 'child_process';
3
+ import util from 'util';
4
+
5
+ import {
6
+ dryRun,
7
+ chatGpt,
8
+ anthropic,
9
+ vertexAi,
10
+ vertexAiClaude,
11
+ disableInitialLint,
12
+ helpRequested,
13
+ } from '../cli/cli-params.js';
14
+ import { validateCliParams } from '../cli/validate-cli-params.js';
15
+ import { generateContent as generateContentVertexAi } from '../ai-service/vertex-ai.js';
16
+ import { generateContent as generateContentGPT } from '../ai-service/chat-gpt.js';
17
+ import { generateContent as generateContentAnthropic } from '../ai-service/anthropic.js';
18
+ import { generateContent as generateContentVertexAiClaude } from '../ai-service/vertex-ai-claude.js';
19
+ import { promptService } from '../prompt/prompt-service.js';
20
+ import { updateFiles } from '../files/update-files.js';
21
+ import { rcConfig } from '../files/find-files.js';
22
+ import { getLintFixPrompt } from '../prompt/prompt-codegen.js';
23
+ import { printHelpMessage } from '../cli/cli-options.js';
24
+
25
+ const execPromise = util.promisify(exec);
26
+
27
+ /** Executes codegen */
28
+ export async function runCodegen() {
29
+ // Print to console the received parameters
30
+ console.log(`Received parameters: ${process.argv.slice(2).join(' ')}`);
31
+
32
+ validateCliParams();
33
+
34
+ // Handle --help option
35
+ if (helpRequested) {
36
+ printHelpMessage();
37
+ return;
38
+ }
39
+
40
+ if (rcConfig.lintCommand && !disableInitialLint) {
41
+ try {
42
+ console.log(`Executing lint command: ${rcConfig.lintCommand}`);
43
+ await execPromise(rcConfig.lintCommand);
44
+ console.log('Lint command executed successfully');
45
+ } catch (error) {
46
+ console.log(
47
+ 'Lint command failed. Aborting codegen, please fix lint issues before running codegen, or use --disable-initial-lint',
48
+ );
49
+ console.log('Lint errors:', error.stdout, error.stderr);
50
+ process.exit(1);
51
+ }
52
+ } else if (rcConfig.lintCommand && disableInitialLint) {
53
+ console.log('Initial lint was skipped.');
54
+ }
55
+
56
+ const generateContent = vertexAiClaude
57
+ ? generateContentVertexAiClaude
58
+ : vertexAi
59
+ ? generateContentVertexAi
60
+ : anthropic
61
+ ? generateContentAnthropic
62
+ : chatGpt
63
+ ? generateContentGPT
64
+ : assert(false, 'Please specify which AI service should be used');
65
+
66
+ console.log('Generating response');
67
+ let functionCalls = await promptService(generateContent);
68
+ console.log('Received function calls:', functionCalls);
69
+
70
+ if (dryRun) {
71
+ console.log('Dry run mode, not updating files');
72
+ } else {
73
+ console.log('Update files');
74
+ await updateFiles(functionCalls.filter((call) => call.name !== 'explanation' && call.name !== 'getSourceCode'));
75
+ console.log('Initial updates applied');
76
+
77
+ // Check if lintCommand is specified in .genaicoderc
78
+ if (rcConfig.lintCommand) {
79
+ try {
80
+ console.log(`Executing lint command: ${rcConfig.lintCommand}`);
81
+ await execPromise(rcConfig.lintCommand);
82
+ console.log('Lint command executed successfully');
83
+ } catch (error) {
84
+ console.log('Lint command failed. Attempting to fix issues...');
85
+
86
+ // Prepare the lint error output for the second pass
87
+ const lintErrorPrompt = getLintFixPrompt(rcConfig.lintCommand, error.stdout, error.stderr);
88
+
89
+ console.log('Generating response for lint fixes');
90
+ const lintFixFunctionCalls = await promptService(generateContent, lintErrorPrompt);
91
+
92
+ console.log('Received function calls for lint fixes:', lintFixFunctionCalls);
93
+
94
+ console.log('Applying lint fixes');
95
+ updateFiles(
96
+ lintFixFunctionCalls.filter((call) => call.name !== 'explanation' && call.name !== 'getSourceCode'),
97
+ );
98
+
99
+ // Run lint command again to verify fixes
100
+ try {
101
+ console.log(`Re-running lint command: ${rcConfig.lintCommand}`);
102
+ await execPromise(rcConfig.lintCommand);
103
+ console.log('Lint command executed successfully after fixes');
104
+ } catch (secondLintError) {
105
+ console.log('Lint command still failing after fixes. Manual intervention may be required.');
106
+ console.log('Lint errors:', secondLintError.stdout, secondLintError.stderr);
107
+ }
108
+ }
109
+ }
110
+
111
+ console.log('Done!');
112
+ }
113
+ }
@@ -0,0 +1,186 @@
1
+ /*eslint-disable no-import-assign*/
2
+
3
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
4
+ import { runCodegen } from './codegen.js';
5
+ import * as vertexAi from '../ai-service/vertex-ai.js';
6
+ import * as chatGpt from '../ai-service/chat-gpt.js';
7
+ import * as anthropic from '../ai-service/anthropic.js';
8
+ import * as vertexAiClaude from '../ai-service/vertex-ai-claude.js';
9
+ import * as updateFiles from '../files/update-files.js';
10
+ import '../files/find-files.js';
11
+ import * as cliParams from '../cli/cli-params.js';
12
+ import * as cliOptions from '../cli/cli-options.js';
13
+
14
+ vi.mock('../ai-service/vertex-ai.js', () => ({ generateContent: vi.fn() }));
15
+ vi.mock('../ai-service/chat-gpt.js', () => ({ generateContent: vi.fn() }));
16
+ vi.mock('../ai-service/anthropic.js', () => ({ generateContent: vi.fn() }));
17
+ vi.mock('../ai-service/vertex-ai-claude.js', () => ({ generateContent: vi.fn() }));
18
+ vi.mock('../files/update-files.js');
19
+ vi.mock('../cli/cli-params.js', () => ({
20
+ requireExplanations: false,
21
+ considerAllFiles: false,
22
+ dependencyTree: false,
23
+ explicitPrompt: false,
24
+ allowFileCreate: false,
25
+ allowFileDelete: false,
26
+ allowDirectoryCreate: false,
27
+ allowFileMove: false,
28
+ verbosePrompt: false,
29
+ vertexAiClaude: false,
30
+ helpRequested: false,
31
+ vision: false,
32
+ imagen: false,
33
+ temperature: 0.7,
34
+ }));
35
+ vi.mock('../files/find-files.js', () => ({
36
+ rootDir: '/mocked/root/dir',
37
+ rcConfig: {
38
+ rootDir: '.',
39
+ extensions: ['.js', '.ts', '.tsx', '.jsx'],
40
+ },
41
+ getSourceFiles: () => [],
42
+ getImageAssetFiles: () => [],
43
+ }));
44
+ vi.mock('../cli/cli-options.js', () => ({
45
+ printHelpMessage: vi.fn(),
46
+ }));
47
+
48
+ describe('runCodegen', () => {
49
+ beforeEach(() => {
50
+ vi.resetAllMocks();
51
+ cliParams.anthropic = false;
52
+ cliParams.chatGpt = false;
53
+ cliParams.vertexAi = false;
54
+ cliParams.vertexAiClaude = false;
55
+ cliParams.dryRun = false;
56
+ cliParams.helpRequested = false;
57
+ cliParams.vision = false;
58
+ });
59
+
60
+ it('should run codegen with Vertex AI by default', async () => {
61
+ cliParams.vertexAi = true;
62
+
63
+ const mockFunctionCalls = [
64
+ { name: 'updateFile', args: { filePath: 'test.js', newContent: 'console.log("Hello");' } },
65
+ ];
66
+ vertexAi.generateContent.mockResolvedValueOnce(mockFunctionCalls);
67
+
68
+ await runCodegen();
69
+
70
+ expect(vertexAi.generateContent).toHaveBeenCalled();
71
+ expect(updateFiles.updateFiles).toHaveBeenCalledWith(mockFunctionCalls);
72
+ });
73
+
74
+ it('should run codegen with ChatGPT when chatGpt flag is true', async () => {
75
+ cliParams.chatGpt = true;
76
+
77
+ const mockFunctionCalls = [{ name: 'createFile', args: { filePath: 'new.js', newContent: 'const x = 5;' } }];
78
+ chatGpt.generateContent.mockResolvedValueOnce(mockFunctionCalls);
79
+
80
+ await runCodegen();
81
+
82
+ expect(chatGpt.generateContent).toHaveBeenCalled();
83
+ expect(updateFiles.updateFiles).toHaveBeenCalledWith(mockFunctionCalls);
84
+ });
85
+
86
+ it('should run codegen with Anthropic when anthropic flag is true', async () => {
87
+ cliParams.anthropic = true;
88
+
89
+ const mockFunctionCalls = [{ name: 'deleteFile', args: { filePath: 'obsolete.js' } }];
90
+ anthropic.generateContent.mockResolvedValueOnce(mockFunctionCalls);
91
+
92
+ await runCodegen();
93
+
94
+ expect(anthropic.generateContent).toHaveBeenCalled();
95
+ expect(updateFiles.updateFiles).toHaveBeenCalledWith(mockFunctionCalls);
96
+ });
97
+
98
+ it('should not update files in dry run mode', async () => {
99
+ cliParams.vertexAi = true;
100
+ cliParams.dryRun = true;
101
+
102
+ const mockFunctionCalls = [
103
+ { name: 'updateFile', args: { filePath: 'test.js', newContent: 'console.log("Dry run");' } },
104
+ ];
105
+ vertexAi.generateContent.mockResolvedValueOnce(mockFunctionCalls);
106
+
107
+ await runCodegen();
108
+
109
+ expect(vertexAi.generateContent).toHaveBeenCalled();
110
+ expect(updateFiles.updateFiles).not.toHaveBeenCalled();
111
+ });
112
+
113
+ it('should run codegen with Vertex AI Claude when vertexAiClaude flag is true', async () => {
114
+ cliParams.vertexAiClaude = true;
115
+
116
+ const mockFunctionCalls = [
117
+ { name: 'updateFile', args: { filePath: 'test.js', newContent: 'console.log("Hello from Claude");' } },
118
+ ];
119
+ vertexAiClaude.generateContent.mockResolvedValueOnce(mockFunctionCalls);
120
+
121
+ await runCodegen();
122
+
123
+ expect(vertexAiClaude.generateContent).toHaveBeenCalled();
124
+ expect(updateFiles.updateFiles).toHaveBeenCalledWith(mockFunctionCalls);
125
+ });
126
+
127
+ it('should pass the temperature parameter to the AI service', async () => {
128
+ cliParams.vertexAi = true;
129
+ cliParams.temperature = 0.5;
130
+
131
+ const mockFunctionCalls = [
132
+ { name: 'updateFile', args: { filePath: 'test.js', newContent: 'console.log("Temperature test");' } },
133
+ ];
134
+ vertexAi.generateContent.mockResolvedValueOnce(mockFunctionCalls);
135
+
136
+ await runCodegen();
137
+
138
+ expect(vertexAi.generateContent).toHaveBeenCalledWith(expect.anything(), expect.anything(), expect.anything(), 0.5);
139
+ expect(updateFiles.updateFiles).toHaveBeenCalledWith(mockFunctionCalls);
140
+ });
141
+
142
+ it('should print help message and not run codegen when --help option is provided', async () => {
143
+ cliParams.helpRequested = true;
144
+
145
+ await runCodegen();
146
+
147
+ expect(cliOptions.printHelpMessage).toHaveBeenCalled();
148
+ expect(vertexAi.generateContent).not.toHaveBeenCalled();
149
+ expect(chatGpt.generateContent).not.toHaveBeenCalled();
150
+ expect(anthropic.generateContent).not.toHaveBeenCalled();
151
+ expect(vertexAiClaude.generateContent).not.toHaveBeenCalled();
152
+ expect(updateFiles.updateFiles).not.toHaveBeenCalled();
153
+ });
154
+
155
+ it('should run codegen with vision when vision flag is true', async () => {
156
+ cliParams.chatGpt = true;
157
+ cliParams.vision = true;
158
+
159
+ const mockFunctionCalls = [
160
+ { name: 'updateFile', args: { filePath: 'test.js', newContent: 'console.log("Vision test");' } },
161
+ ];
162
+ chatGpt.generateContent.mockResolvedValueOnce(mockFunctionCalls);
163
+
164
+ await runCodegen();
165
+
166
+ expect(chatGpt.generateContent).toHaveBeenCalled();
167
+ expect(updateFiles.updateFiles).toHaveBeenCalledWith(mockFunctionCalls);
168
+ // Check if the generateContent function was called with the correct parameters
169
+ expect(chatGpt.generateContent.mock.calls[0][0]).toEqual(
170
+ expect.arrayContaining([
171
+ expect.objectContaining({
172
+ type: 'user',
173
+ text: expect.stringContaining('I should also provide you with a summary of application image assets'),
174
+ }),
175
+ expect.objectContaining({
176
+ type: 'assistant',
177
+ text: expect.stringContaining('Please provide summary of application image assets.'),
178
+ }),
179
+ expect.objectContaining({
180
+ type: 'user',
181
+ functionResponses: [{ name: 'getImageAssets', content: expect.any(String) }],
182
+ }),
183
+ ]),
184
+ );
185
+ });
186
+ });
@@ -0,0 +1,23 @@
1
+ import assert from 'node:assert';
2
+
3
+ const SYSTEM_PROMPT_LIMIT = 200;
4
+ const CODEGEN_PROMPT_LIMIT = 500;
5
+ const SOURCE_CODE_LIMIT = 20000;
6
+
7
+ function verifyPromptLimit(promptType, prompt, limit) {
8
+ const tokenCount = prompt.split(/\s+/).length;
9
+ console.log(`${promptType} prompt token count: ${tokenCount}`);
10
+ assert(tokenCount <= limit, `Token limit exceeded: ${tokenCount} > ${limit}`);
11
+ }
12
+
13
+ export function verifySystemPromptLimit(systemPrompt) {
14
+ verifyPromptLimit('system', systemPrompt, SYSTEM_PROMPT_LIMIT);
15
+ }
16
+
17
+ export function verifyCodegenPromptLimit(codeGenPrompt) {
18
+ verifyPromptLimit('codegen', codeGenPrompt, CODEGEN_PROMPT_LIMIT);
19
+ }
20
+
21
+ export function verifySourceCodeLimit(sourceCode) {
22
+ verifyPromptLimit('sourceCode', sourceCode, SOURCE_CODE_LIMIT);
23
+ }
@@ -0,0 +1,40 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { verifySystemPromptLimit, verifyCodegenPromptLimit, verifySourceCodeLimit } from './limits.js';
3
+
4
+ describe('Prompt Limits', () => {
5
+ describe('verifySystemPromptLimit', () => {
6
+ it('should not throw for a prompt within the limit', () => {
7
+ const validPrompt = 'A '.repeat(200 - 1);
8
+ expect(() => verifySystemPromptLimit(validPrompt)).not.toThrow();
9
+ });
10
+
11
+ it('should throw for a prompt exceeding the limit', () => {
12
+ const invalidPrompt = 'A '.repeat(201);
13
+ expect(() => verifySystemPromptLimit(invalidPrompt)).toThrow('Token limit exceeded: 202 > 200');
14
+ });
15
+ });
16
+
17
+ describe('verifyCodegenPromptLimit', () => {
18
+ it('should not throw for a prompt within the limit', () => {
19
+ const validPrompt = 'B '.repeat(200 - 1);
20
+ expect(() => verifyCodegenPromptLimit(validPrompt)).not.toThrow();
21
+ });
22
+
23
+ it('should throw for a prompt exceeding the limit', () => {
24
+ const invalidPrompt = 'B '.repeat(501);
25
+ expect(() => verifyCodegenPromptLimit(invalidPrompt)).toThrow('Token limit exceeded: 502 > 500');
26
+ });
27
+ });
28
+
29
+ describe('verifySourceCodeLimit', () => {
30
+ it('should not throw for source code within the limit', () => {
31
+ const validSourceCode = 'C '.repeat(20000 - 1);
32
+ expect(() => verifySourceCodeLimit(validSourceCode)).not.toThrow();
33
+ });
34
+
35
+ it('should throw for source code exceeding the limit', () => {
36
+ const invalidSourceCode = 'C '.repeat(20001);
37
+ expect(() => verifySourceCodeLimit(invalidSourceCode)).toThrow('Token limit exceeded: 20002 > 20000');
38
+ });
39
+ });
40
+ });
@@ -0,0 +1,106 @@
1
+ import assert from 'node:assert';
2
+ import { getSourceCode } from '../files/read-files.js';
3
+ import { CODEGEN_TRIGGER } from './prompt-consts.js';
4
+ import {
5
+ considerAllFiles,
6
+ allowFileCreate,
7
+ allowFileDelete,
8
+ allowDirectoryCreate,
9
+ allowFileMove,
10
+ explicitPrompt,
11
+ dependencyTree,
12
+ verbosePrompt,
13
+ vision,
14
+ imagen,
15
+ } from '../cli/cli-params.js';
16
+ import { getDependencyList } from '../files/find-files.js';
17
+ import { verifyCodegenPromptLimit } from './limits.js';
18
+
19
+ /** Get codegen prompt */
20
+ export function getCodeGenPrompt() {
21
+ let codeGenFiles;
22
+ if (considerAllFiles) {
23
+ codeGenFiles = Object.keys(getSourceCode());
24
+ } else {
25
+ codeGenFiles = Object.entries(getSourceCode())
26
+ .filter(([, content]) => content.match(new RegExp("([^'^`]+)" + CODEGEN_TRIGGER)))
27
+ .map(([path]) => path);
28
+ }
29
+
30
+ // Add logic to consider dependency tree
31
+ if (dependencyTree) {
32
+ assert(codeGenFiles.length > 0, `You must use ${CODEGEN_TRIGGER} together with --dependency-tree`);
33
+
34
+ const dependencyTreeFiles = new Set();
35
+ codeGenFiles
36
+ .map(getDependencyList)
37
+ .flat()
38
+ .forEach((key) => dependencyTreeFiles.add(key));
39
+ codeGenFiles = Array.from(dependencyTreeFiles);
40
+ }
41
+
42
+ const codeGenPrompt =
43
+ (explicitPrompt ? explicitPrompt + '\n\n' : '') +
44
+ `${
45
+ considerAllFiles
46
+ ? codeGenFiles.length > 0
47
+ ? `I have marked some files with the ${CODEGEN_TRIGGER} fragments:\n${codeGenFiles.join('\n')}`
48
+ : `No files are marked with ${CODEGEN_TRIGGER} fragment, so you can consider doing changes in any file.`
49
+ : `Generate updates only for the following files:\n${codeGenFiles.join('\n')}`
50
+ }
51
+
52
+ ${
53
+ considerAllFiles
54
+ ? 'You are allowed to modify all files in the application regardless if they contain codegen fragments or not.'
55
+ : 'Do not modify files which do not contain the fragments.'
56
+ }
57
+ ${allowFileCreate ? 'You are allowed to create new files.' : 'Do not create new files.'}
58
+ ${
59
+ allowFileDelete
60
+ ? 'You are allowed to delete files, in such case add empty string as content.'
61
+ : 'Do not delete files.'
62
+ }
63
+ ${allowDirectoryCreate ? 'You are allowed to create new directories.' : 'Do not create new directories.'}
64
+ ${allowFileMove ? 'You are allowed to move files.' : 'Do not move files.'}
65
+ ${vision ? 'You are allowed to analyze image assets.' : 'Do not analyze image assets.'}
66
+ ${imagen ? 'You are allowed to generate images.' : 'You are not allowed to generate images.'}
67
+ `;
68
+
69
+ if (verbosePrompt) {
70
+ console.log('Code gen prompt:');
71
+ console.log(codeGenPrompt);
72
+ }
73
+
74
+ verifyCodegenPromptLimit(codeGenPrompt);
75
+
76
+ return codeGenPrompt;
77
+ }
78
+
79
+ /** Get lint fix prompt */
80
+ export function getLintFixPrompt(command, stdout, stderr) {
81
+ const lintFixPrompt = `The following lint errors were encountered after the initial code generation:
82
+
83
+ Lint command: ${command}
84
+ Lint command stdout:
85
+
86
+ \`\`\`
87
+ ${stdout}
88
+ \`\`\`
89
+
90
+ Lint command stderr:
91
+
92
+ \`\`\`
93
+ ${stderr}
94
+ \`\`\`
95
+
96
+ Please suggest changes to fix these lint errors.`;
97
+
98
+ if (verbosePrompt) {
99
+ console.log('Lint fix prompt:');
100
+ console.log(lintFixPrompt);
101
+ }
102
+
103
+ verifyCodegenPromptLimit(lintFixPrompt);
104
+
105
+ return lintFixPrompt;
106
+ }