cloving 0.3.19 → 0.3.20

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 (95) hide show
  1. package/dist/index.js +2 -0
  2. package/dist/managers/CommitManager.js +30 -16
  3. package/dist/package.json +1 -1
  4. package/package.json +1 -1
  5. package/.DS_Store +0 -0
  6. package/.aider.chat.history.md +0 -16978
  7. package/.aider.input.history +0 -15
  8. package/.aider.tags.cache.v3/cache.db +0 -0
  9. package/.aider.tags.cache.v3/cache.db-shm +0 -0
  10. package/.aider.tags.cache.v3/cache.db-wal +0 -0
  11. package/.cursorrules +0 -77
  12. package/.github/workflows/auto-release.yml +0 -83
  13. package/.github/workflows/ci.yml +0 -32
  14. package/.github/workflows/npm-publish.yml +0 -34
  15. package/.gitignore +0 -67
  16. package/.husky/_/.gitignore +0 -1
  17. package/.husky/_/applypatch-msg +0 -2
  18. package/.husky/_/commit-msg +0 -2
  19. package/.husky/_/h +0 -22
  20. package/.husky/_/husky.sh +0 -9
  21. package/.husky/_/post-applypatch +0 -2
  22. package/.husky/_/post-checkout +0 -2
  23. package/.husky/_/post-commit +0 -2
  24. package/.husky/_/post-merge +0 -2
  25. package/.husky/_/post-rewrite +0 -2
  26. package/.husky/_/pre-applypatch +0 -2
  27. package/.husky/_/pre-auto-gc +0 -2
  28. package/.husky/_/pre-commit +0 -2
  29. package/.husky/_/pre-merge-commit +0 -2
  30. package/.husky/_/pre-push +0 -2
  31. package/.husky/_/pre-rebase +0 -2
  32. package/.husky/_/prepare-commit-msg +0 -2
  33. package/.husky/pre-commit +0 -1
  34. package/.husky/pre-push +0 -1
  35. package/.prettierrc.json +0 -7
  36. package/.tool-versions +0 -2
  37. package/SECURITY.md +0 -15
  38. package/cloving.json +0 -67
  39. package/jest.config.js +0 -11
  40. package/src/cloving_gpt/adapters/azureopenai.ts +0 -83
  41. package/src/cloving_gpt/adapters/claude.ts +0 -140
  42. package/src/cloving_gpt/adapters/gemini.ts +0 -146
  43. package/src/cloving_gpt/adapters/index.ts +0 -43
  44. package/src/cloving_gpt/adapters/mistral.ts +0 -142
  45. package/src/cloving_gpt/adapters/ollama.ts +0 -136
  46. package/src/cloving_gpt/adapters/openai.ts +0 -113
  47. package/src/cloving_gpt/index.ts +0 -229
  48. package/src/commands/chat.ts +0 -12
  49. package/src/commands/config.ts +0 -192
  50. package/src/commands/generate/code.ts +0 -18
  51. package/src/commands/generate/commit.ts +0 -9
  52. package/src/commands/generate/context.ts +0 -93
  53. package/src/commands/generate/docs.ts +0 -9
  54. package/src/commands/generate/review.ts +0 -9
  55. package/src/commands/generate/shell.ts +0 -9
  56. package/src/commands/generate/unit_tests.ts +0 -9
  57. package/src/commands/init.ts +0 -9
  58. package/src/commands/models.ts +0 -21
  59. package/src/commands/proxy.ts +0 -9
  60. package/src/commands/tokens.ts +0 -82
  61. package/src/index.ts +0 -197
  62. package/src/managers/BlockManager.ts +0 -126
  63. package/src/managers/ChatManager.ts +0 -755
  64. package/src/managers/ChunkManager.ts +0 -31
  65. package/src/managers/CodeManager.ts +0 -226
  66. package/src/managers/CommitManager.ts +0 -71
  67. package/src/managers/DocsManager.ts +0 -101
  68. package/src/managers/InitManager.ts +0 -263
  69. package/src/managers/ProxyManager.ts +0 -240
  70. package/src/managers/ReviewManager.ts +0 -154
  71. package/src/managers/ShellManager.ts +0 -172
  72. package/src/managers/StreamManager.ts +0 -353
  73. package/src/managers/UnitTestManager.ts +0 -293
  74. package/src/utils/config_utils.ts +0 -271
  75. package/src/utils/git_utils.ts +0 -230
  76. package/src/utils/model_utils.ts +0 -3
  77. package/src/utils/prompt_utils.ts +0 -375
  78. package/src/utils/prompts/CODEGEN_COULDNT_APPLY.md +0 -5
  79. package/src/utils/prompts/CODEGEN_EXAMPLES.md +0 -82
  80. package/src/utils/prompts/CODEGEN_INSTRUCTIONS.md +0 -43
  81. package/src/utils/prompts/DOCS_INSTRUCTIONS.md +0 -9
  82. package/src/utils/prompts/INIT_INSTRUCTIONS.md +0 -132
  83. package/src/utils/prompts/REVIEW_INSTRUCTIONS.md +0 -49
  84. package/src/utils/prompts/SHELL_INSTRUCTIONS.md +0 -15
  85. package/src/utils/prompts/SPECIAL_FILES.md +0 -66
  86. package/src/utils/prompts/index.ts +0 -37
  87. package/src/utils/string_utils.ts +0 -552
  88. package/src/utils/types.ts +0 -127
  89. package/tests/commands/generate/unit_tests.test.ts +0 -27
  90. package/tests/utils/git_utils.test.ts +0 -116
  91. package/tests/utils/model_utils.test.ts +0 -27
  92. package/tests/utils/prompt_utils.test.ts +0 -33
  93. package/tests/utils/string_utils.test.ts +0 -560
  94. package/tsconfig.json +0 -14
  95. package/yarn.lock +0 -5823
@@ -1,353 +0,0 @@
1
- import fs from 'fs'
2
- import axios from 'axios'
3
- import path from 'path'
4
- import { EventEmitter } from 'events'
5
- import colors from 'colors'
6
- import highlight from 'cli-highlight'
7
-
8
- import ClovingGPT from '../cloving_gpt'
9
- import ChunkManager from './ChunkManager'
10
- import BlockManager from './BlockManager'
11
-
12
- import { getPackageVersion } from '../utils/prompt_utils'
13
- import { getNearestClovingConfig } from '../utils/config_utils'
14
- import { addFileOrDirectoryToContext, generateCodegenPrompt } from '../utils/prompt_utils'
15
- import type { CurrentNewBlock, ChatMessage } from '../utils/types'
16
-
17
- /**
18
- * StreamManager class
19
- *
20
- * This class extends EventEmitter and manages the streaming of responses from an AI model.
21
- * It handles the processing of chunks, manages code blocks, and provides animation for code generation.
22
- */
23
- class StreamManager extends EventEmitter {
24
- protected gpt: ClovingGPT
25
- protected prompt: string = ''
26
- protected responseString: string = ''
27
- protected contextFiles: Record<string, string> = {}
28
- protected chatHistory: ChatMessage[] = []
29
- protected chunkManager: ChunkManager
30
- protected blockManager: BlockManager
31
- protected isProcessing: boolean = false
32
- protected animationInterval: NodeJS.Timeout | null = null
33
- protected retryCount: number = 0
34
- protected maxRetries: number = 3
35
-
36
- /**
37
- * Creates an instance of StreamManager.
38
- * @param {any} options - Configuration options for the StreamManager.
39
- */
40
- constructor(protected options: any) {
41
- super()
42
- this.gpt = new ClovingGPT(options)
43
- this.chunkManager = new ChunkManager()
44
- this.blockManager = new BlockManager()
45
- }
46
-
47
- /**
48
- * Checks for the latest version of the app from GitHub and compares it with the current version.
49
- * If a new version is available, it notifies the user.
50
- * @private
51
- * @returns {Promise<void>}
52
- */
53
- protected async checkForLatestVersion(): Promise<void> {
54
- try {
55
- const response = await axios.get(
56
- 'https://api.github.com/repos/cloving-ai/cloving-cli/releases/latest',
57
- )
58
- const latestVersion = response.data.tag_name
59
- const currentVersion = `v${getPackageVersion()}`
60
-
61
- if (latestVersion !== currentVersion) {
62
- console.log(
63
- colors.bgWhite.black(`\n 🚀 A new version of Cloving is available: ${latestVersion} `),
64
- )
65
- console.log(
66
- colors.bgWhite.black(` You are currently using version: ${currentVersion} `),
67
- )
68
- console.log(
69
- colors.bgWhite.black(
70
- ` To upgrade, run: ${colors.bgWhite.black.bold('npm install -g cloving@latest')} `,
71
- ),
72
- )
73
- }
74
- } catch (error) {
75
- // ignore errors
76
- }
77
- }
78
-
79
- /**
80
- * Handles the response stream from the AI model.
81
- * @param {any} responseStream - The stream of responses from the AI model.
82
- */
83
- protected handleResponseStream(responseStream: any) {
84
- this.responseString = ''
85
- this.setupChunkManagerEvents()
86
- this.setupBlockManagerEvents()
87
- this.setupResponseStreamEvents(responseStream)
88
- }
89
-
90
- /**
91
- * Sets up event listeners for the BlockManager.
92
- * @private
93
- */
94
- private setupBlockManagerEvents(): void {
95
- this.blockManager = new BlockManager()
96
-
97
- this.blockManager.on('startGeneratingCode', () => {
98
- this.setupAnimationInterval()
99
- })
100
-
101
- this.blockManager.on('endGeneratingCode', () => {
102
- this.clearAnimation()
103
- })
104
-
105
- this.blockManager.on('content', (content: string) => {
106
- process.stdout.write(content)
107
- this.responseString += content
108
- })
109
-
110
- this.blockManager.on(
111
- 'codeBlock',
112
- (codeBlock: { currentNewBlock?: CurrentNewBlock; raw?: string }) => {
113
- this.handleCodeBlock(codeBlock)
114
- },
115
- )
116
- }
117
-
118
- /**
119
- * Sets up an animation interval for code generation visualization.
120
- * @private
121
- */
122
- private setupAnimationInterval(): void {
123
- process.stdout.write(colors.gray.bold('\nGenerating code... '))
124
- let animationIndex = 0
125
- const animationChars = ['/', '|', '\\', '-']
126
- this.animationInterval = setInterval(() => {
127
- process.stdout.write('\b'.repeat(1))
128
- process.stdout.write(colors.gray.bold(animationChars[animationIndex]))
129
- animationIndex = (animationIndex + 1) % animationChars.length
130
- }, 100)
131
- }
132
-
133
- /**
134
- * Clears the animation interval.
135
- * @private
136
- */
137
- private clearAnimation(): void {
138
- if (!this.animationInterval) return
139
- clearInterval(this.animationInterval)
140
- process.stdout.write('\b'.repeat(100))
141
- }
142
-
143
- /**
144
- * Handles the processing of a code block.
145
- * @private
146
- * @param {Object} codeBlock - The code block to process.
147
- * @param {CurrentNewBlock} [codeBlock.currentNewBlock] - The current and new content of the code block.
148
- * @param {string} [codeBlock.raw] - The raw content of the code block.
149
- */
150
- private handleCodeBlock(codeBlock: { currentNewBlock?: CurrentNewBlock; raw?: string }): void {
151
- const { currentNewBlock, raw } = codeBlock
152
- if (currentNewBlock?.currentContent && currentNewBlock?.newContent) {
153
- this.displayCurrentNewBlock(currentNewBlock)
154
- } else if (raw) {
155
- this.displayRawCodeBlock(raw)
156
- } else {
157
- process.stdout.write(` \n`)
158
- }
159
-
160
- this.responseString += raw || currentNewBlock
161
- }
162
-
163
- /**
164
- * Displays the current and new content of a code block.
165
- * @private
166
- * @param {CurrentNewBlock} currentNewBlock - The current and new content of the code block.
167
- */
168
- private displayCurrentNewBlock(currentNewBlock: CurrentNewBlock): void {
169
- const language = currentNewBlock.language.split('\n')[0]
170
- process.stdout.write(
171
- colors.gray.bold(`\`\`\`${language} \n`),
172
- )
173
- process.stdout.write(colors.gray.bold(`<<<<<<< CURRENT ${currentNewBlock.filePath}\n`))
174
- try {
175
- process.stdout.write(highlight(currentNewBlock.currentContent))
176
- } catch (error) {
177
- process.stdout.write(currentNewBlock.currentContent)
178
- }
179
- process.stdout.write(colors.gray.bold('\n=======\n'))
180
- try {
181
- process.stdout.write(highlight(currentNewBlock.newContent))
182
- } catch (error) {
183
- process.stdout.write(currentNewBlock.newContent)
184
- }
185
- process.stdout.write(colors.gray.bold('\n>>>>>>> NEW\n```'))
186
- }
187
-
188
- /**
189
- * Displays a raw code block.
190
- * @private
191
- * @param {string} raw - The raw content of the code block.
192
- */
193
- private displayRawCodeBlock(raw: string): void {
194
- process.stdout.write(` \n`)
195
- process.stdout.write(highlight(raw))
196
- }
197
-
198
- /**
199
- * Sets up event listeners for the ChunkManager.
200
- * @private
201
- */
202
- private setupChunkManagerEvents(): void {
203
- this.chunkManager = new ChunkManager()
204
- this.chunkManager.on('content', (buffer: string) => {
205
- this.processChunkManagerContent(buffer)
206
- })
207
- }
208
-
209
- /**
210
- * Processes the content received from the ChunkManager.
211
- * @private
212
- * @param {string} buffer - The buffer containing the chunk content.
213
- */
214
- private processChunkManagerContent(buffer: string): void {
215
- let convertedStream = this.gpt.convertStream(buffer)
216
-
217
- while (convertedStream !== null) {
218
- const { output, lastChar } = convertedStream
219
- this.blockManager.addContent(output)
220
- this.chunkManager.clearBuffer(lastChar)
221
- buffer = buffer.slice(lastChar)
222
- convertedStream = this.gpt.convertStream(buffer)
223
- }
224
- }
225
-
226
- /**
227
- * Sets up event listeners for the response stream.
228
- * @private
229
- * @param {any} responseStream - The stream of responses from the AI model.
230
- */
231
- private setupResponseStreamEvents(responseStream: any): void {
232
- responseStream.data.on('data', this.handleResponseStreamData.bind(this))
233
- responseStream.data.on('end', () => this.finalizeResponse())
234
- responseStream.data.on('error', this.handleResponseStreamError.bind(this))
235
- }
236
-
237
- /**
238
- * Handles the data received from the response stream.
239
- * @private
240
- * @param {Buffer | undefined} chunk - The chunk of data received from the stream.
241
- */
242
- private handleResponseStreamData(chunk: Buffer | undefined): void {
243
- if (!chunk) return
244
- const chunkString = chunk.toString()
245
- this.chunkManager.addChunk(chunkString)
246
- }
247
-
248
- /**
249
- * Handles errors that occur during the response stream.
250
- * @private
251
- * @param {Error} error - The error that occurred.
252
- */
253
- private handleResponseStreamError(error: Error): void {
254
- console.error('Error streaming response:', error)
255
- this.isProcessing = false
256
- process.stdout.write('\n')
257
- this.emit('prompt')
258
- }
259
-
260
- /**
261
- * Finalizes the response processing.
262
- * This method should be overridden in subclasses to provide specific finalization behavior.
263
- * @protected
264
- */
265
- protected finalizeResponse(): void {
266
- this.isProcessing = false
267
- this.emit('responseFinalized', this.responseString)
268
- }
269
-
270
- /**
271
- * Loads context files specified in the options or defaults to the primary language's directory.
272
- * @private
273
- * @returns {Promise<void>}
274
- */
275
- protected async loadContextFiles(): Promise<void> {
276
- try {
277
- const config = getNearestClovingConfig().config
278
- const primaryLanguage = config.languages.find((lang) => lang.primary)
279
- const defaultDirectory = primaryLanguage ? primaryLanguage.directory : '.'
280
- const testingDirectories =
281
- config.testingFrameworks?.map((framework) => framework.directory) || []
282
- const testingDirectory = testingDirectories[0]
283
-
284
- let files = this.options.files || [defaultDirectory, testingDirectory].filter(Boolean)
285
- if (files.length > 0) {
286
- console.log(`\nBuilding AI prompt context...\n`)
287
- }
288
- for (const file of files) {
289
- // Skip if the file is not a string
290
- if (!file) continue
291
-
292
- try {
293
- const previousCount = Object.keys(this.contextFiles).length
294
- this.contextFiles = await addFileOrDirectoryToContext(
295
- file,
296
- this.contextFiles,
297
- this.options,
298
- )
299
- const newCount = Object.keys(this.contextFiles).length
300
- const addedCount = newCount - previousCount
301
-
302
- const totalTokens = this.calculateTotalTokens()
303
-
304
- console.log(colors.cyan(`📁 Loaded context from: ${colors.bold(file)}`))
305
- console.log(colors.green(` ✅ Added ${addedCount} file(s) to context`))
306
- console.log(
307
- colors.yellow(` 📊 Total tokens in context: ${totalTokens.toLocaleString()}\n`),
308
- )
309
- } catch (fileError) {
310
- console.error(colors.red(`Error loading context from ${file}:`), fileError)
311
- }
312
- }
313
- } catch (error) {
314
- console.error(colors.red('Error loading context files:'), error)
315
- throw new Error('Failed to load context files')
316
- }
317
- }
318
-
319
- protected calculateTotalTokens(): number {
320
- return Object.values(this.contextFiles).reduce((total, content) => {
321
- return total + Math.ceil(content.length / 4)
322
- }, 0)
323
- }
324
-
325
- protected async refreshContext() {
326
- await this.reloadContextFiles()
327
- const updatedSystemPrompt = generateCodegenPrompt(this.contextFiles)
328
- this.chatHistory[0] = { role: 'user', content: updatedSystemPrompt }
329
- this.chatHistory[1] = { role: 'assistant', content: 'What would you like to do?' }
330
- }
331
-
332
- protected async reloadContextFiles() {
333
- for (const filePath in this.contextFiles) {
334
- try {
335
- const content = await fs.promises.readFile(path.resolve(filePath), 'utf8')
336
- this.contextFiles[filePath] = content
337
- } catch (error) {
338
- console.error(`Error reading file ${filePath}:`, error)
339
- }
340
- }
341
- }
342
-
343
- protected addUserPrompt(content: string) {
344
- this.prompt = content
345
- this.chatHistory.push({ role: 'user', content })
346
- }
347
-
348
- protected addAssistantResponse(content: string) {
349
- this.chatHistory.push({ role: 'assistant', content })
350
- }
351
- }
352
-
353
- export default StreamManager
@@ -1,293 +0,0 @@
1
- import { promises as fs } from 'fs'
2
- import { execFileSync } from 'child_process'
3
- import highlight from 'cli-highlight'
4
- import { select, confirm } from '@inquirer/prompts'
5
-
6
- import { getGitDiff } from '../utils/git_utils'
7
- import { CODEGEN_INSTRUCTIONS } from '../utils/prompts'
8
- import { getTestingDirectory, getAllFiles } from '../utils/config_utils'
9
- import {
10
- extractCurrentNewBlocks,
11
- applyAndSaveCurrentNewBlocks,
12
- extractMarkdown,
13
- } from '../utils/string_utils'
14
- import ClovingGPT from '../cloving_gpt'
15
- import type { ClovingGPTOptions, ChatMessage } from '../utils/types'
16
-
17
- class UnitTestManager {
18
- private gpt: ClovingGPT
19
- private chatHistory: ChatMessage[] = []
20
-
21
- constructor(private options: ClovingGPTOptions) {
22
- this.gpt = new ClovingGPT(options)
23
- }
24
-
25
- private extractChangedFiles(gitDiff: string): string[] {
26
- const fileRegex = /diff --git a\/(.+?) b\/(.+?)\n/g
27
- const files = new Set<string>()
28
- let match
29
-
30
- while ((match = fileRegex.exec(gitDiff)) !== null) {
31
- files.add(match[1])
32
- }
33
-
34
- return Array.from(files)
35
- }
36
-
37
- private generateUnitTestPrompt(
38
- files: string[],
39
- srcFiles: string,
40
- testFiles: string,
41
- gitDiff?: string,
42
- ): string {
43
- if (this.chatHistory.length === 0) {
44
- const systemPrompt = `### Example of a well-structured response
45
-
46
- # Unit Test Generation
47
-
48
- ## Files Overview
49
-
50
- These are the files that are relevant for generating unit tests:
51
-
52
- 1. **app/models/foo.rb**
53
- 2. **app/views/baz/bar.html.erb**
54
-
55
- ## Relevant Test Files
56
-
57
- 1. **test/models/foo_test.rb**
58
- 2. **test/controllers/baz_controller_test.rb**
59
-
60
- ## Task
61
-
62
- Generate unit tests for the files listed above. Always show filenames for the generated code.`
63
-
64
- this.chatHistory.push({ role: 'user', content: systemPrompt })
65
- this.chatHistory.push({ role: 'assistant', content: 'What would you like to do?' })
66
- }
67
-
68
- const filesList = files.length > 0 ? files.join('\n') : 'No files provided'
69
- const prompt = `Enumerate all the files in the provided list${gitDiff ? ' and git diff' : ''} as well as the file names of anything that these files interact with.
70
- Also, list any test files that might be relevant to these files.
71
-
72
- ### List of Code Files
73
-
74
- ${filesList}
75
-
76
- ### List of All Source Files
77
-
78
- ${srcFiles}
79
-
80
- ### List of all test files
81
-
82
- ${testFiles}
83
-
84
- ${gitDiff ? '### Git Diff Content' : ''}
85
-
86
- ${gitDiff || ''}
87
-
88
- ## Example Output
89
-
90
- Here are the files that are relevant:
91
-
92
- \`\`\`plaintext
93
- ## files
94
-
95
- app/models/foo.rb
96
- app/views/baz/bar.html.erb
97
-
98
- ## relevant test files
99
-
100
- test/models/foo_test.rb
101
- test/controllers/baz_controller_test.rb
102
- \`\`\`
103
-
104
- ### Prompt
105
-
106
- Enumerate all the files in the provided list${gitDiff ? ' and git diff' : ''} as well as the file names of anything that these files interact with.
107
- Also, list any test files that might be relevant to these files.`
108
- this.chatHistory.push({ role: 'user', content: prompt })
109
- return prompt
110
- }
111
-
112
- private async handleUserAction(analysis: string, autoSave: boolean = false): Promise<void> {
113
- const blocks = extractCurrentNewBlocks(analysis)
114
-
115
- if (autoSave) {
116
- await applyAndSaveCurrentNewBlocks(blocks)
117
- return
118
- }
119
-
120
- const files = Array.from(new Set(blocks.map((block) => block.filePath)))
121
-
122
- const action = await select({
123
- message: 'What would you like to do?',
124
- choices: [
125
- { name: 'Save a Unit Test File', value: 'save' },
126
- { name: 'Save All Unit Test Files', value: 'saveAll' },
127
- { name: 'Copy Entire Response to Clipboard', value: 'copyAll' },
128
- { name: 'Done', value: 'done' },
129
- ],
130
- })
131
-
132
- switch (action) {
133
- case 'save':
134
- let saveAnother = true
135
- while (saveAnother) {
136
- const fileToSave = await select({
137
- message: 'Which unit test file do you want to save?',
138
- choices: files.map((file) => ({ name: file, value: file })),
139
- })
140
-
141
- await applyAndSaveCurrentNewBlocks(
142
- blocks.filter((block) => block.filePath === fileToSave),
143
- )
144
-
145
- saveAnother = await confirm({
146
- message: 'Do you want to save another file?',
147
- default: false,
148
- })
149
- }
150
- break
151
- case 'saveAll':
152
- await applyAndSaveCurrentNewBlocks(blocks)
153
- console.log('All unit test files have been saved.')
154
- break
155
- case 'copyAll':
156
- process.nextTick(() => {
157
- require('copy-paste').copy(analysis, () => {
158
- console.log('Entire response copied to clipboard')
159
- })
160
- })
161
- break
162
- case 'done':
163
- break
164
- }
165
- }
166
-
167
- public async generateUnitTests() {
168
- const { files, save } = this.options
169
-
170
- const allSrcFiles = await getAllFiles(this.options, true)
171
- const testingDirectory = getTestingDirectory()
172
-
173
- if (allSrcFiles.length === 0 || !testingDirectory) {
174
- console.error(
175
- 'Could not find any source files to generate unit tests. Please run: cloving init. Then try again.',
176
- )
177
- process.exit(1)
178
- }
179
-
180
- const testFiles = execFileSync('find', [testingDirectory, '-type', 'f']).toString().trim()
181
- let contextFiles: string
182
-
183
- if (files) {
184
- console.log('Generating unit tests for the provided files...')
185
- console.log('Gathering a list of files related to the changes...')
186
- const prompt = this.generateUnitTestPrompt(files, allSrcFiles.join('\n'), testFiles)
187
- contextFiles = await this.gpt.generateText({ prompt, messages: this.chatHistory })
188
- } else {
189
- console.log(
190
- 'Generating unit tests for the git diff between this branch and the default branch...',
191
- )
192
- console.log('Gathering a list of files related to the changes...')
193
- const gitDiff = await getGitDiff()
194
- const changedFiles = this.extractChangedFiles(gitDiff)
195
- const prompt = this.generateUnitTestPrompt(
196
- changedFiles,
197
- allSrcFiles.join('\n'),
198
- testFiles,
199
- gitDiff,
200
- )
201
- contextFiles = await this.gpt.generateText({ prompt, messages: this.chatHistory })
202
- }
203
-
204
- this.chatHistory.push({ role: 'assistant', content: contextFiles })
205
- contextFiles = extractMarkdown(contextFiles)
206
-
207
- console.log(contextFiles)
208
- console.log('\nBuilding the tests for these files...')
209
-
210
- // Initialize variables
211
- const lines: string[] = []
212
- const context: string[] = []
213
-
214
- // Read each line of the context files
215
- contextFiles.split('\n').forEach((line) => {
216
- if (line.trim()) {
217
- lines.push(line.trim())
218
- }
219
- })
220
-
221
- for (const codeFile of lines) {
222
- if (
223
- await fs
224
- .stat(codeFile)
225
- .then((stat) => stat.isFile())
226
- .catch(() => false)
227
- ) {
228
- const fileContents = await fs.readFile(codeFile, 'utf-8')
229
- context.push(
230
- `### Contents of **${codeFile}\n\n${fileContents
231
- .split('\n')
232
- .map((line) => ` ${line}`)
233
- .join('\n')}**\n\n`,
234
- )
235
- }
236
- }
237
-
238
- // Generate the message for unit test creation
239
- let analysis = ''
240
-
241
- if ((files || []).length > 0) {
242
- const message = `Create unit tests for the List of Files. Always show filenames for the generated code.
243
-
244
- ## List of Files
245
- ${(files || []).join('\n')}
246
-
247
- ${CODEGEN_INSTRUCTIONS}
248
-
249
- ## Context
250
-
251
- ${context.join('\n\n')}
252
-
253
- ## Prompt
254
-
255
- Create unit tests for the List of Files. Always show filenames for the generated code.`
256
-
257
- // Get the model and analysis using ClovingGPT
258
- this.chatHistory.push({ role: 'user', content: message })
259
- analysis = await this.gpt.generateText({ prompt: message, messages: this.chatHistory })
260
- } else {
261
- const message = `Create unit tests for the Code Diff. Always show filenames for the generated code.
262
-
263
- ## Code Diff
264
-
265
- ${await getGitDiff()}
266
-
267
- ## Context
268
-
269
- ${context.join('\n\n')}
270
-
271
- ${CODEGEN_INSTRUCTIONS}
272
-
273
- ## Prompt
274
-
275
- Create unit tests for the Code Diff. Always show filenames for the generated code.`
276
-
277
- // Get the model and analysis using ClovingGPT
278
- this.chatHistory.push({ role: 'user', content: message })
279
- analysis = await this.gpt.generateText({ prompt: message, messages: this.chatHistory })
280
- }
281
-
282
- if (analysis) {
283
- console.log(highlight(analysis))
284
- } else {
285
- console.log('No unit tests generated.')
286
- }
287
-
288
- // Add the new user action handling
289
- await this.handleUserAction(analysis, save)
290
- }
291
- }
292
-
293
- export default UnitTestManager