cloving 0.3.18 → 0.3.19
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.
- package/.DS_Store +0 -0
- package/.aider.chat.history.md +16978 -0
- package/.aider.input.history +15 -0
- package/.aider.tags.cache.v3/cache.db +0 -0
- package/.aider.tags.cache.v3/cache.db-shm +0 -0
- package/.aider.tags.cache.v3/cache.db-wal +0 -0
- package/.cursorrules +77 -0
- package/.github/workflows/auto-release.yml +83 -0
- package/.github/workflows/ci.yml +32 -0
- package/.github/workflows/npm-publish.yml +34 -0
- package/.gitignore +67 -0
- package/.husky/_/.gitignore +1 -0
- package/.husky/_/applypatch-msg +2 -0
- package/.husky/_/commit-msg +2 -0
- package/.husky/_/h +22 -0
- package/.husky/_/husky.sh +9 -0
- package/.husky/_/post-applypatch +2 -0
- package/.husky/_/post-checkout +2 -0
- package/.husky/_/post-commit +2 -0
- package/.husky/_/post-merge +2 -0
- package/.husky/_/post-rewrite +2 -0
- package/.husky/_/pre-applypatch +2 -0
- package/.husky/_/pre-auto-gc +2 -0
- package/.husky/_/pre-commit +2 -0
- package/.husky/_/pre-merge-commit +2 -0
- package/.husky/_/pre-push +2 -0
- package/.husky/_/pre-rebase +2 -0
- package/.husky/_/prepare-commit-msg +2 -0
- package/.husky/pre-commit +1 -0
- package/.husky/pre-push +1 -0
- package/.prettierrc.json +7 -0
- package/.tool-versions +2 -0
- package/SECURITY.md +15 -0
- package/cloving.json +67 -0
- package/dist/managers/CommitManager.js +8 -2
- package/dist/package.json +1 -1
- package/jest.config.js +11 -0
- package/package.json +1 -1
- package/src/cloving_gpt/adapters/azureopenai.ts +83 -0
- package/src/cloving_gpt/adapters/claude.ts +140 -0
- package/src/cloving_gpt/adapters/gemini.ts +146 -0
- package/src/cloving_gpt/adapters/index.ts +43 -0
- package/src/cloving_gpt/adapters/mistral.ts +142 -0
- package/src/cloving_gpt/adapters/ollama.ts +136 -0
- package/src/cloving_gpt/adapters/openai.ts +113 -0
- package/src/cloving_gpt/index.ts +229 -0
- package/src/commands/chat.ts +12 -0
- package/src/commands/config.ts +192 -0
- package/src/commands/generate/code.ts +18 -0
- package/src/commands/generate/commit.ts +9 -0
- package/src/commands/generate/context.ts +93 -0
- package/src/commands/generate/docs.ts +9 -0
- package/src/commands/generate/review.ts +9 -0
- package/src/commands/generate/shell.ts +9 -0
- package/src/commands/generate/unit_tests.ts +9 -0
- package/src/commands/init.ts +9 -0
- package/src/commands/models.ts +21 -0
- package/src/commands/proxy.ts +9 -0
- package/src/commands/tokens.ts +82 -0
- package/src/index.ts +197 -0
- package/src/managers/BlockManager.ts +126 -0
- package/src/managers/ChatManager.ts +755 -0
- package/src/managers/ChunkManager.ts +31 -0
- package/src/managers/CodeManager.ts +226 -0
- package/src/managers/CommitManager.ts +71 -0
- package/src/managers/DocsManager.ts +101 -0
- package/src/managers/InitManager.ts +263 -0
- package/src/managers/ProxyManager.ts +240 -0
- package/src/managers/ReviewManager.ts +154 -0
- package/src/managers/ShellManager.ts +172 -0
- package/src/managers/StreamManager.ts +353 -0
- package/src/managers/UnitTestManager.ts +293 -0
- package/src/utils/config_utils.ts +271 -0
- package/src/utils/git_utils.ts +230 -0
- package/src/utils/model_utils.ts +3 -0
- package/src/utils/prompt_utils.ts +375 -0
- package/src/utils/prompts/CODEGEN_COULDNT_APPLY.md +5 -0
- package/src/utils/prompts/CODEGEN_EXAMPLES.md +82 -0
- package/src/utils/prompts/CODEGEN_INSTRUCTIONS.md +43 -0
- package/src/utils/prompts/DOCS_INSTRUCTIONS.md +9 -0
- package/src/utils/prompts/INIT_INSTRUCTIONS.md +132 -0
- package/src/utils/prompts/REVIEW_INSTRUCTIONS.md +49 -0
- package/src/utils/prompts/SHELL_INSTRUCTIONS.md +15 -0
- package/src/utils/prompts/SPECIAL_FILES.md +66 -0
- package/src/utils/prompts/index.ts +37 -0
- package/src/utils/string_utils.ts +552 -0
- package/src/utils/types.ts +127 -0
- package/tests/commands/generate/unit_tests.test.ts +27 -0
- package/tests/utils/git_utils.test.ts +116 -0
- package/tests/utils/model_utils.test.ts +27 -0
- package/tests/utils/prompt_utils.test.ts +33 -0
- package/tests/utils/string_utils.test.ts +560 -0
- package/tsconfig.json +14 -0
- package/yarn.lock +5823 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module prompt_utils
|
|
3
|
+
* @description Provides utility functions for generating prompts, handling files, and running commands.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'fs'
|
|
7
|
+
import path from 'path'
|
|
8
|
+
import { execSync, spawn } from 'child_process'
|
|
9
|
+
import { isBinaryFile } from 'isbinaryfile'
|
|
10
|
+
import colors from 'colors'
|
|
11
|
+
import { join } from 'path'
|
|
12
|
+
|
|
13
|
+
import { getNearestClovingConfig } from './config_utils'
|
|
14
|
+
import {
|
|
15
|
+
REVIEW_INSTRUCTIONS,
|
|
16
|
+
DOCS_INSTRUCTIONS,
|
|
17
|
+
CODEGEN_INSTRUCTIONS,
|
|
18
|
+
SPECIAL_FILES,
|
|
19
|
+
SHELL_INSTRUCTIONS,
|
|
20
|
+
CODEGEN_EXAMPLES,
|
|
21
|
+
} from './prompts'
|
|
22
|
+
|
|
23
|
+
import type { ClovingGPTOptions } from './types'
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Retrieves the package version from package.json.
|
|
27
|
+
* @returns {string} The version of the package.
|
|
28
|
+
*/
|
|
29
|
+
export const getPackageVersion = () => {
|
|
30
|
+
const packagePath = join(__dirname, '..', 'package.json')
|
|
31
|
+
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'))
|
|
32
|
+
return packageJson.version
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Renders a context file as a string.
|
|
37
|
+
* @param {Record<string, unknown> | string} contextFile - The context file to render.
|
|
38
|
+
* @returns {string} The rendered string representation of the context file.
|
|
39
|
+
*/
|
|
40
|
+
const renderAsString = (contextFile: Record<string, unknown> | string): string => {
|
|
41
|
+
if (typeof contextFile === 'string') {
|
|
42
|
+
return contextFile
|
|
43
|
+
}
|
|
44
|
+
return JSON.stringify(contextFile, null, 2)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Generates a prompt for code generation based on context files and special files.
|
|
49
|
+
* @param {Record<string, string>} contextFilesContent - The content of context files.
|
|
50
|
+
* @returns {string} The generated prompt for code generation.
|
|
51
|
+
*/
|
|
52
|
+
export const generateCodegenPrompt = (contextFilesContent: Record<string, string>): string => {
|
|
53
|
+
const specialFileContents = collectSpecialFileContents()
|
|
54
|
+
// detect if specialFileContents[file] is a string or an object
|
|
55
|
+
const specialFiles = Object.keys(specialFileContents)
|
|
56
|
+
.map(
|
|
57
|
+
(file) =>
|
|
58
|
+
`### Contents of **${file}**\n\n\`\`\`\n${renderAsString(specialFileContents[file])}\n\`\`\`\n\n`,
|
|
59
|
+
)
|
|
60
|
+
.join('\n')
|
|
61
|
+
const contextFileContents = Object.keys(contextFilesContent)
|
|
62
|
+
.map(
|
|
63
|
+
(file) => `### Contents of **${file}**\n\n\`\`\`\n${contextFilesContent[file]}\n\`\`\`\n\n`,
|
|
64
|
+
)
|
|
65
|
+
.join('\n')
|
|
66
|
+
|
|
67
|
+
const prompt = `${CODEGEN_INSTRUCTIONS}
|
|
68
|
+
|
|
69
|
+
## Description of App
|
|
70
|
+
|
|
71
|
+
\`\`\`json
|
|
72
|
+
${JSON.stringify(getNearestClovingConfig().config, null, 2)}
|
|
73
|
+
\`\`\`
|
|
74
|
+
|
|
75
|
+
## Special Files
|
|
76
|
+
|
|
77
|
+
${specialFiles.length > 0 ? specialFiles : 'No special files provided.'}
|
|
78
|
+
|
|
79
|
+
## Context Files
|
|
80
|
+
|
|
81
|
+
${contextFileContents.length > 0 ? contextFileContents : 'No context files provided.'}
|
|
82
|
+
|
|
83
|
+
## Directory structure
|
|
84
|
+
|
|
85
|
+
${Object.keys(contextFilesContent).join('\n')}
|
|
86
|
+
|
|
87
|
+
${CODEGEN_INSTRUCTIONS}
|
|
88
|
+
|
|
89
|
+
${CODEGEN_EXAMPLES}`
|
|
90
|
+
return prompt
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Generates a prompt for documentation generation based on context files and special files.
|
|
95
|
+
* @param {Record<string, string>} contextFilesContent - The content of context files.
|
|
96
|
+
* @returns {string} The generated prompt for documentation generation.
|
|
97
|
+
*/
|
|
98
|
+
export const generateDocsPrompt = (contextFilesContent: Record<string, string>): string => {
|
|
99
|
+
const specialFileContents = collectSpecialFileContents()
|
|
100
|
+
const specialFiles = Object.keys(specialFileContents)
|
|
101
|
+
.map(
|
|
102
|
+
(file) =>
|
|
103
|
+
`### Contents of **${file}**\n\n\`\`\`\n${renderAsString(specialFileContents[file])}\n\`\`\`\n\n`,
|
|
104
|
+
)
|
|
105
|
+
.join('\n')
|
|
106
|
+
const contextFileContents = Object.keys(contextFilesContent)
|
|
107
|
+
.map(
|
|
108
|
+
(file) => `### Contents of **${file}**\n\n\`\`\`\n${contextFilesContent[file]}\n\`\`\`\n\n`,
|
|
109
|
+
)
|
|
110
|
+
.join('\n')
|
|
111
|
+
|
|
112
|
+
const prompt = `${DOCS_INSTRUCTIONS}
|
|
113
|
+
|
|
114
|
+
${CODEGEN_INSTRUCTIONS}
|
|
115
|
+
|
|
116
|
+
${CODEGEN_EXAMPLES}
|
|
117
|
+
|
|
118
|
+
## Description of App
|
|
119
|
+
|
|
120
|
+
\`\`\`json
|
|
121
|
+
${JSON.stringify(getNearestClovingConfig().config, null, 2)}
|
|
122
|
+
\`\`\`
|
|
123
|
+
|
|
124
|
+
## Special Files
|
|
125
|
+
|
|
126
|
+
${specialFiles.length > 0 ? specialFiles : 'No special files provided.'}
|
|
127
|
+
|
|
128
|
+
## Context Files
|
|
129
|
+
|
|
130
|
+
${contextFileContents.length > 0 ? contextFileContents : 'No context files provided.'}
|
|
131
|
+
|
|
132
|
+
## Directory structure
|
|
133
|
+
|
|
134
|
+
${Object.keys(contextFilesContent).join('\n')}
|
|
135
|
+
|
|
136
|
+
${DOCS_INSTRUCTIONS}`
|
|
137
|
+
|
|
138
|
+
return prompt
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Generates a prompt for shell commands based on the current shell and OS.
|
|
143
|
+
* @returns {string} The generated prompt for shell commands.
|
|
144
|
+
*/
|
|
145
|
+
export const generateShellPrompt = (): string => {
|
|
146
|
+
const shell = execSync('echo $SHELL').toString().trim()
|
|
147
|
+
const os = execSync('echo $OSTYPE').toString().trim()
|
|
148
|
+
return `${SHELL_INSTRUCTIONS}\n\n## Context\n\nShell: ${shell}\n\nOS: ${os}`
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const generateReviewPrompt = (
|
|
152
|
+
options: ClovingGPTOptions,
|
|
153
|
+
contextFiles: Record<string, string>,
|
|
154
|
+
): string => {
|
|
155
|
+
let prompt = `${REVIEW_INSTRUCTIONS}`
|
|
156
|
+
if (options.files) {
|
|
157
|
+
const contextFileContents = Object.keys(contextFiles)
|
|
158
|
+
.map((file) => `### Contents of ${file}\n\n${contextFiles[file]}\n\n`)
|
|
159
|
+
.join('\n')
|
|
160
|
+
|
|
161
|
+
prompt += `### Description of App
|
|
162
|
+
|
|
163
|
+
${JSON.stringify(getNearestClovingConfig().config, null, 2)}
|
|
164
|
+
|
|
165
|
+
${contextFileContents}
|
|
166
|
+
|
|
167
|
+
### Request
|
|
168
|
+
|
|
169
|
+
I would like you to explain the code and document a description of it.
|
|
170
|
+
List any bugs in the new code as well as recommended fixes for those bugs with code examples.
|
|
171
|
+
Format the output of this code review in Markdown format.`
|
|
172
|
+
} else {
|
|
173
|
+
prompt += `### Request
|
|
174
|
+
|
|
175
|
+
Do not use any data from the example response structure, only use the structure.
|
|
176
|
+
I would like you to explain why these change are being made and document a description of these changes.
|
|
177
|
+
Also list any bugs in the new code as well as recommended fixes for those bugs with code examples.
|
|
178
|
+
Format the output of this code review in Markdown format.`
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return prompt
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Collects the contents of special files.
|
|
186
|
+
* @returns {Record<string, string | Record<string, unknown>>} An object containing the contents of special files.
|
|
187
|
+
*/
|
|
188
|
+
export const collectSpecialFileContents = (): Record<string, string | Record<string, unknown>> => {
|
|
189
|
+
const specialFileContents: Record<string, string | Record<string, unknown>> = {}
|
|
190
|
+
for (const file of SPECIAL_FILES) {
|
|
191
|
+
if (fs.existsSync(file)) {
|
|
192
|
+
try {
|
|
193
|
+
const content = fs.readFileSync(file, 'utf-8')
|
|
194
|
+
specialFileContents[file] = file.endsWith('.json') ? JSON.parse(content) : content
|
|
195
|
+
} catch (error) {
|
|
196
|
+
specialFileContents[file] = fs.readFileSync(file, 'utf-8')
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return specialFileContents
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Checks if any special files exist in the current directory.
|
|
205
|
+
* @returns {boolean} True if any special files exist, false otherwise.
|
|
206
|
+
*/
|
|
207
|
+
export const checkForSpecialFiles = (): boolean => SPECIAL_FILES.some((file) => fs.existsSync(file))
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Recursively gets all non-binary files in a directory.
|
|
211
|
+
* @param {string} dir - The directory to search.
|
|
212
|
+
* @returns {Promise<string[]>} A promise that resolves with an array of file paths.
|
|
213
|
+
*/
|
|
214
|
+
export const getAllFilesInDirectory = async (dir: string): Promise<string[]> => {
|
|
215
|
+
const subdirs = await fs.promises.readdir(dir)
|
|
216
|
+
const files = await Promise.all(
|
|
217
|
+
subdirs.map(async (subdir) => {
|
|
218
|
+
const res = path.resolve(dir, subdir)
|
|
219
|
+
if (subdir === 'node_modules' || subdir === '.git' || subdir === '.DS_Store') {
|
|
220
|
+
return []
|
|
221
|
+
}
|
|
222
|
+
const stat = await fs.promises.stat(res)
|
|
223
|
+
if (stat.isDirectory()) {
|
|
224
|
+
return getAllFilesInDirectory(res)
|
|
225
|
+
} else {
|
|
226
|
+
const isBinary = await isBinaryFile(res)
|
|
227
|
+
return isBinary ? [] : res
|
|
228
|
+
}
|
|
229
|
+
}),
|
|
230
|
+
)
|
|
231
|
+
return files.flat()
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Generates a list of files in the current directory and its subdirectories.
|
|
236
|
+
* @returns {Promise<string[]>} A promise that resolves with an array of file paths.
|
|
237
|
+
*/
|
|
238
|
+
export const generateFileList = async (): Promise<string[]> => {
|
|
239
|
+
try {
|
|
240
|
+
const lsOutput = await runCommand('ls', [])
|
|
241
|
+
const findOutput = await runCommand('find', ['.'])
|
|
242
|
+
const cleanedFindOutput = findOutput.map((file) =>
|
|
243
|
+
file.startsWith('./') ? file.slice(2) : file,
|
|
244
|
+
)
|
|
245
|
+
const files = [...lsOutput, ...cleanedFindOutput]
|
|
246
|
+
const uniqueFiles = Array.from(new Set(files))
|
|
247
|
+
return uniqueFiles.filter(
|
|
248
|
+
(file) =>
|
|
249
|
+
file && !file.includes('.git') && !file.includes('node_modules') && !file.includes('tmp'),
|
|
250
|
+
)
|
|
251
|
+
} catch (error) {
|
|
252
|
+
console.error('Error generating file list:', (error as Error).message)
|
|
253
|
+
return []
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Runs a shell command and returns its output.
|
|
259
|
+
* @param {string} command - The command to run.
|
|
260
|
+
* @param {string[]} args - The arguments for the command.
|
|
261
|
+
* @returns {Promise<string[]>} A promise that resolves with an array of output lines.
|
|
262
|
+
*/
|
|
263
|
+
export const runCommand = (command: string, args: string[]): Promise<string[]> => {
|
|
264
|
+
return new Promise((resolve, reject) => {
|
|
265
|
+
const process = spawn(command, args)
|
|
266
|
+
const output: string[] = []
|
|
267
|
+
|
|
268
|
+
process.stdout.on('data', (data) => {
|
|
269
|
+
output.push(data.toString())
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
process.stderr.on('data', (data) => {
|
|
273
|
+
console.error(`stderr: ${data}`)
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
process.on('close', (code) => {
|
|
277
|
+
if (code !== 0) {
|
|
278
|
+
reject(new Error(`${command} process exited with code ${code}`))
|
|
279
|
+
} else {
|
|
280
|
+
resolve(output.join('').trim().split('\n'))
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Fetches available models using the 'cloving models' command.
|
|
288
|
+
* @returns {Promise<string[]>} A promise that resolves with an array of available models.
|
|
289
|
+
*/
|
|
290
|
+
export const fetchModels = async (): Promise<string[]> => {
|
|
291
|
+
try {
|
|
292
|
+
const modelsOutput = await runCommand('cloving', ['models'])
|
|
293
|
+
return modelsOutput
|
|
294
|
+
} catch (error) {
|
|
295
|
+
console.error('Error fetching models:', (error as Error).message)
|
|
296
|
+
return []
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Reads the content of a file.
|
|
302
|
+
* @param {string} file - The path to the file.
|
|
303
|
+
* @returns {string} The content of the file.
|
|
304
|
+
*/
|
|
305
|
+
export const readFileContent = (file: string): string => {
|
|
306
|
+
try {
|
|
307
|
+
return fs.readFileSync(file, 'utf-8')
|
|
308
|
+
} catch (error) {
|
|
309
|
+
console.error('Error reading file content:', (error as Error).message)
|
|
310
|
+
return ''
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const addFileToContext = async (
|
|
315
|
+
filePath: string,
|
|
316
|
+
contextFiles: Record<string, string>,
|
|
317
|
+
baseDir: string,
|
|
318
|
+
): Promise<void> => {
|
|
319
|
+
const relativePath = path.relative(baseDir, filePath)
|
|
320
|
+
if (await isBinaryFile(filePath)) {
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
const content = await fs.promises.readFile(filePath, 'utf-8')
|
|
324
|
+
contextFiles[relativePath] = content
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const addDirectoryToContext = async (
|
|
328
|
+
dirPath: string,
|
|
329
|
+
contextFiles: Record<string, string>,
|
|
330
|
+
baseDir: string,
|
|
331
|
+
): Promise<void> => {
|
|
332
|
+
const entries = await fs.promises.readdir(dirPath, { withFileTypes: true })
|
|
333
|
+
for (const entry of entries) {
|
|
334
|
+
const fullPath = path.join(dirPath, entry.name)
|
|
335
|
+
if (entry.isDirectory()) {
|
|
336
|
+
if (entry.name !== 'node_modules' && entry.name !== '.git') {
|
|
337
|
+
await addDirectoryToContext(fullPath, contextFiles, baseDir)
|
|
338
|
+
}
|
|
339
|
+
} else {
|
|
340
|
+
await addFileToContext(fullPath, contextFiles, baseDir)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Adds a file or directory to the context.
|
|
347
|
+
* @param {string} contextFile - The path to the file or directory to add.
|
|
348
|
+
* @param {Record<string, string>} contextFiles - The current context files.
|
|
349
|
+
* @param {Record<string, any>} options - Additional options.
|
|
350
|
+
* @returns {Promise<Record<string, string>>} A promise that resolves with the updated context files.
|
|
351
|
+
*/
|
|
352
|
+
export const addFileOrDirectoryToContext = async (
|
|
353
|
+
contextFile: string,
|
|
354
|
+
contextFiles: Record<string, string>,
|
|
355
|
+
options: Record<string, any>,
|
|
356
|
+
): Promise<Record<string, string>> => {
|
|
357
|
+
const filePath = path.resolve(contextFile)
|
|
358
|
+
const baseDir = process.cwd()
|
|
359
|
+
|
|
360
|
+
try {
|
|
361
|
+
const stats = await fs.promises.stat(filePath)
|
|
362
|
+
if (stats.isDirectory()) {
|
|
363
|
+
await addDirectoryToContext(filePath, contextFiles, baseDir)
|
|
364
|
+
} else if (stats.isFile()) {
|
|
365
|
+
await addFileToContext(filePath, contextFiles, baseDir)
|
|
366
|
+
}
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error(
|
|
369
|
+
colors.red(`${colors.bold('Error')}: File or directory "${contextFile}" does not exist`),
|
|
370
|
+
)
|
|
371
|
+
process.exit(1)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return contextFiles
|
|
375
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
## Code Generation Failed
|
|
2
|
+
|
|
3
|
+
Some of the provided code blocks could not be applied, please match the existing code with a few more lines of context and make sure it is a character for character exact match. Never use "rest of the existing code" or "rest of the code" in your response, instead break the code block into smaller blocks.
|
|
4
|
+
|
|
5
|
+
## Details
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
### Example 1: Changing a Variable
|
|
2
|
+
|
|
3
|
+
```typescript
|
|
4
|
+
<<<<<<< CURRENT path/to/file.ts
|
|
5
|
+
const myVariable = 'exact'
|
|
6
|
+
const oldVariable = 1
|
|
7
|
+
=======
|
|
8
|
+
const myVariable = 'exact'
|
|
9
|
+
const newVariable = 2
|
|
10
|
+
>>>>>>> NEW
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Example 2: Create a New File
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
<<<<<<< CURRENT path/to/newfile.rb
|
|
17
|
+
=======
|
|
18
|
+
def new_function
|
|
19
|
+
puts 'Hello, world!'
|
|
20
|
+
end
|
|
21
|
+
>>>>>>> NEW
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Example 3: Moving a Function
|
|
25
|
+
|
|
26
|
+
Removing it from path/to/file.py
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
<<<<<<< CURRENT path/to/file.py
|
|
30
|
+
def old_function():
|
|
31
|
+
return 'Old function'
|
|
32
|
+
=======
|
|
33
|
+
>>>>>>> NEW
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Adding it to path/to/newfile.py
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
<<<<<<< CURRENT path/to/newfile.py
|
|
40
|
+
def existing_function():
|
|
41
|
+
return 'Existing function'
|
|
42
|
+
|
|
43
|
+
=======
|
|
44
|
+
def existing_function():
|
|
45
|
+
return 'Existing function'
|
|
46
|
+
|
|
47
|
+
def old_function():
|
|
48
|
+
return 'Old function'
|
|
49
|
+
>>>>>>> NEW
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Example 4: Removing a Function
|
|
53
|
+
|
|
54
|
+
```java
|
|
55
|
+
<<<<<<< CURRENT path/to/file.java
|
|
56
|
+
public void oldFunction() {
|
|
57
|
+
System.out.println("Old function")
|
|
58
|
+
}
|
|
59
|
+
=======
|
|
60
|
+
>>>>>>> NEW
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Example 5: Replace a file
|
|
64
|
+
|
|
65
|
+
This will replace the entire contents of **path/to/file.rb** with the new content:
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
<<<<<<< CURRENT path/to/file.rb
|
|
69
|
+
=======
|
|
70
|
+
def new_function
|
|
71
|
+
puts 'Hello, world!'
|
|
72
|
+
end
|
|
73
|
+
>>>>>>> NEW
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Don't Invent Code That Isn't Provided in Context
|
|
77
|
+
|
|
78
|
+
A file might be referenced by just the file name without the full path, but the code must be provided in the *Context Files* section.
|
|
79
|
+
|
|
80
|
+
Unless you believe you are creating a whole new file, never ever under any circumstances make up code in the CURRENT block that was not provided to you in the *Context Files* section.
|
|
81
|
+
|
|
82
|
+
If a required file hasn't been provided in the *Context Files* section, stop everything and ask the user to provide it in the context by adding -f path/to/file to the command or add path/to/file in the interactive chat.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# AI Code Generation Instructions
|
|
2
|
+
|
|
3
|
+
## General Guidelines
|
|
4
|
+
|
|
5
|
+
1. **Respond as an expert software developer.**
|
|
6
|
+
2. **Follow best practices and the latest standards found in the **Description of App** section.**
|
|
7
|
+
3. **Adhere to existing conventions and libraries in the codebase.**
|
|
8
|
+
|
|
9
|
+
## Request Handling
|
|
10
|
+
|
|
11
|
+
1. **Try to understand the request, but if anything is unclear, ask questions.**
|
|
12
|
+
2. **Decide if you need to propose edits to existing files not in the chat.**
|
|
13
|
+
- If yes, provide the full path names and ask the user to add them.
|
|
14
|
+
- Wait for user approval before proceeding.
|
|
15
|
+
3. **Propose changes using *CURRENT/NEW* Blocks.**
|
|
16
|
+
4. **Show the smallest possible *CURRENT* section that uniquely identifies the code.**
|
|
17
|
+
5. **To move code, use two *CURRENT/NEW* blocks: one to remove and one to add.**
|
|
18
|
+
6. **For new files or to replace an existing file, the *CURRENT* block is empty.**
|
|
19
|
+
|
|
20
|
+
## *CURRENT/NEW* Block Format
|
|
21
|
+
|
|
22
|
+
1. **Start a block with a one line description of change followed by three backticks and the language name like this:**
|
|
23
|
+
About to add documentation to the `UserService` class.
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
2. **Next line: seven <, CURRENT, and the file path like this:**
|
|
27
|
+
<<<<<<< CURRENT path/to/file.ts
|
|
28
|
+
3. **Include the exact existing code to be changed, this section should always have some code characters in it, not just spaces or tabs.**
|
|
29
|
+
4. **Divide with seven = like this:**
|
|
30
|
+
=======
|
|
31
|
+
5. **Add the new code.**
|
|
32
|
+
6. **End with seven > and NEW like this:**
|
|
33
|
+
>>>>>>> NEW
|
|
34
|
+
7. **Close the block with three backticks like this:**
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Don't Invent Code That Isn't Provided in Context
|
|
38
|
+
|
|
39
|
+
A file might be referenced by just the file name without the full path, but the code must be provided in the *Context Files* section.
|
|
40
|
+
|
|
41
|
+
Unless you believe you are creating a whole new file, never ever under any circumstances make up code in the CURRENT block that was not provided to you in the *Context Files* section.
|
|
42
|
+
|
|
43
|
+
If a required file hasn't been provided in the *Context Files* section, stop everything and ask the user to provide it in the context by adding -f path/to/file to the command or add path/to/file in the interactive chat.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
## Documentation Instructions
|
|
2
|
+
|
|
3
|
+
Add documentation to the files in context. Provide the updated files with added documentation using the CURRENT/NEW block format. Only include the parts of the files that have changed.
|
|
4
|
+
Add or update documentation comments for each function, class, and module definition as appropriate. Use the standard documentation format for the respective language (e.g., JSDoc for JavaScript/TypeScript, docstrings for Python, etc.).
|
|
5
|
+
|
|
6
|
+
### Note
|
|
7
|
+
|
|
8
|
+
Whenever possible, break up the changes into pieces and make sure every change is in its own CURRENT / NEW block.
|
|
9
|
+
Do not allow empty CURRENT blocks and if you add content to the CURRENT block, you also have to add the same content to the NEW block.`
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Initialization Instructions
|
|
2
|
+
|
|
3
|
+
You are an AI assistant helping to initialize a project by analyzing its structure and technologies.
|
|
4
|
+
|
|
5
|
+
Please return JSON-formatted metadata about the project, including:
|
|
6
|
+
|
|
7
|
+
- The programming language(s) used
|
|
8
|
+
- The detected framework(s) (if any)
|
|
9
|
+
- The version of the language(s)
|
|
10
|
+
|
|
11
|
+
Here is a typescript interface for the expected response:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
interface LanguageConfig {
|
|
15
|
+
name: string
|
|
16
|
+
version?: string
|
|
17
|
+
primary?: boolean
|
|
18
|
+
directory: string
|
|
19
|
+
extension: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface FrameworkConfig {
|
|
23
|
+
name: string
|
|
24
|
+
type: string
|
|
25
|
+
version?: string
|
|
26
|
+
primary?: boolean
|
|
27
|
+
directory?: string
|
|
28
|
+
extension?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface TestingFrameworkConfig {
|
|
32
|
+
name: string
|
|
33
|
+
type: string
|
|
34
|
+
version?: string
|
|
35
|
+
directory?: string
|
|
36
|
+
runCommand?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface BuildToolConfig {
|
|
40
|
+
name: string
|
|
41
|
+
type: string
|
|
42
|
+
version?: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface LinterConfig {
|
|
46
|
+
name: string
|
|
47
|
+
version?: string
|
|
48
|
+
type?: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface DatabaseConfig {
|
|
52
|
+
name: string
|
|
53
|
+
primary?: boolean
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ClovingfileConfig {
|
|
57
|
+
languages: LanguageConfig[]
|
|
58
|
+
frameworks: FrameworkConfig[]
|
|
59
|
+
testingFrameworks?: TestingFrameworkConfig[]
|
|
60
|
+
buildTools: BuildToolConfig[]
|
|
61
|
+
packageManager: string
|
|
62
|
+
linters: LinterConfig[]
|
|
63
|
+
databases?: DatabaseConfig[]
|
|
64
|
+
projectType: string
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Here is an example response:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"languages": [
|
|
73
|
+
{
|
|
74
|
+
"name": "TypeScript",
|
|
75
|
+
"version": "~> 5.5.3",
|
|
76
|
+
"primary": true,
|
|
77
|
+
"directory": "src",
|
|
78
|
+
"extension": ".ts"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "JavaScript",
|
|
82
|
+
"version": "ES6+",
|
|
83
|
+
"directory": "src",
|
|
84
|
+
"extension": ".js"
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
"frameworks": [
|
|
88
|
+
{
|
|
89
|
+
"name": "Node.js",
|
|
90
|
+
"type": "Runtime environment",
|
|
91
|
+
"primary": true,
|
|
92
|
+
"directory": "src",
|
|
93
|
+
"extension": ".js"
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"testingFrameworks": [
|
|
97
|
+
{
|
|
98
|
+
"name": "Jest",
|
|
99
|
+
"type": "Testing framework",
|
|
100
|
+
"version": "29.7.0",
|
|
101
|
+
"directory": "tests",
|
|
102
|
+
"runCommand": "yarn test"
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"buildTools": [
|
|
106
|
+
{
|
|
107
|
+
"name": "TypeScript Compiler (tsc)",
|
|
108
|
+
"type": "Transpiler"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "Vite",
|
|
112
|
+
"type": "Build tool",
|
|
113
|
+
"version": "5.3.3"
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
"packageManager": "Yarn",
|
|
117
|
+
"linters": [
|
|
118
|
+
{
|
|
119
|
+
"name": "ESLint",
|
|
120
|
+
"version": "9.6.0"
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
"databases": [
|
|
124
|
+
{
|
|
125
|
+
"name": "MongoDB",
|
|
126
|
+
"version": "5.0.3",
|
|
127
|
+
"primary": true
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
"projectType": "Command-line tool"
|
|
131
|
+
}
|
|
132
|
+
```
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
### Example of a well-structured response
|
|
2
|
+
|
|
3
|
+
# Code Review: Enhanced Code Generation and File Handling
|
|
4
|
+
|
|
5
|
+
## Changes Overview
|
|
6
|
+
|
|
7
|
+
These changes primarily focus on improving the code generation process and adding more robust file handling capabilities. The main modifications include:
|
|
8
|
+
|
|
9
|
+
1. Enhancing the code generation prompt with a structured example response.
|
|
10
|
+
2. Implementing a new function to extract file names and contents from the generated code.
|
|
11
|
+
3. Adding a new option to save generated code directly to files.
|
|
12
|
+
4. Improving the copy-to-clipboard functionality to allow copying individual files.
|
|
13
|
+
5. Removing an unused import in the review.ts file.
|
|
14
|
+
|
|
15
|
+
## Reason for Changes
|
|
16
|
+
|
|
17
|
+
The changes aim to enhance the usability and flexibility of the code generation tool by providing clearer prompts, better file management, and more control over the generated code.
|
|
18
|
+
|
|
19
|
+
## Detailed Description
|
|
20
|
+
|
|
21
|
+
1. **Enhanced Code Generation Prompt** - The `generateCodePrompt` function now includes a structured example of a well-formatted response. This helps guide the AI to produce more consistent and usable output, making it easier to parse and use the generated code.
|
|
22
|
+
2. **File Extraction Function** - A new function `extractFilesAndContent` has been added to parse the raw code command and extract individual file names and their contents. This enables more granular control over the generated code, allowing for file-specific operations.
|
|
23
|
+
3. **Save to File Option** - A new 'Save Source Code to Files' option has been added to the user action menu. This feature allows users to save generated code directly to files in the appropriate directory structure.
|
|
24
|
+
4. **Improved Copy to Clipboard** - The 'Copy Source Code to Clipboard' option has been enhanced to allow users to choose specific files to copy, rather than copying all generated code at once.
|
|
25
|
+
5. **Removed Unused Import** - The unused import of `execFileSync` from 'child_process' has been removed from the review.ts file, improving code cleanliness.
|
|
26
|
+
|
|
27
|
+
## Potential Bugs and Recommended Fixes
|
|
28
|
+
|
|
29
|
+
### 1. Possible undefined access in `extractFilesAndContent`
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
const contentMatch = regex.exec(rawCodeCommand)
|
|
33
|
+
if (contentMatch) {
|
|
34
|
+
files.push(fileName)
|
|
35
|
+
let content = contentMatch[1]
|
|
36
|
+
// ...
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
There's a potential for `contentMatch[1]` to be undefined if the regex doesn't capture any groups. To fix this, add a null check:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const contentMatch = regex.exec(rawCodeCommand)
|
|
44
|
+
if (contentMatch && contentMatch[1]) {
|
|
45
|
+
files.push(fileName)
|
|
46
|
+
let content = contentMatch[1]
|
|
47
|
+
// ...
|
|
48
|
+
}
|
|
49
|
+
```
|