cloving 0.3.10 → 0.3.12

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 (34) hide show
  1. package/dist/cloving_gpt/adapters/azureopenai.js +1 -1
  2. package/dist/cloving_gpt/index.js +31 -38
  3. package/dist/commands/config.js +45 -0
  4. package/dist/commands/generate/commit.js +1 -1
  5. package/dist/commands/generate/docs.js +11 -0
  6. package/dist/commands/generate/documentation.js +11 -0
  7. package/dist/commands/generate/review.js +1 -1
  8. package/dist/commands/tokens.js +24 -0
  9. package/dist/index.js +10 -0
  10. package/dist/managers/BlockManager.js +17 -13
  11. package/dist/managers/ChatManager.js +22 -208
  12. package/dist/managers/CodeManager.js +58 -55
  13. package/dist/managers/CommitManager.js +1 -1
  14. package/dist/managers/DocsManager.js +86 -0
  15. package/dist/managers/DocumentationManager.js +114 -0
  16. package/dist/managers/InitManager.js +2 -1
  17. package/dist/managers/ReviewManager.js +59 -169
  18. package/dist/managers/ShellManager.js +49 -0
  19. package/dist/managers/StreamManager.js +291 -0
  20. package/dist/managers/UnitTestManager.js +3 -3
  21. package/dist/package.json +2 -2
  22. package/dist/utils/config_utils.js +43 -0
  23. package/dist/utils/git_utils.js +29 -0
  24. package/dist/utils/prompt_utils.js +125 -307
  25. package/dist/utils/prompts/CODEGEN_COULDNT_APPLY.md +5 -0
  26. package/dist/utils/prompts/CODEGEN_EXAMPLES.md +82 -0
  27. package/dist/utils/prompts/CODEGEN_INSTRUCTIONS.md +42 -0
  28. package/dist/utils/prompts/DOCS_INSTRUCTIONS.md +9 -0
  29. package/dist/utils/prompts/INIT_INSTRUCTIONS.md +132 -0
  30. package/dist/utils/prompts/REVIEW_INSTRUCTIONS.md +49 -0
  31. package/dist/utils/prompts/SHELL_INSTRUCTIONS.md +15 -0
  32. package/dist/utils/prompts/SPECIAL_FILES.md +66 -0
  33. package/dist/utils/prompts/index.js +27 -0
  34. package/package.json +2 -2
@@ -4,21 +4,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const copy_paste_1 = __importDefault(require("copy-paste"));
7
- const readline_1 = __importDefault(require("readline"));
8
- const child_process_1 = require("child_process");
9
7
  const colors_1 = __importDefault(require("colors"));
10
- const axios_1 = __importDefault(require("axios"));
11
8
  const cli_highlight_1 = __importDefault(require("cli-highlight"));
12
- const fs_1 = __importDefault(require("fs"));
13
- const path_1 = __importDefault(require("path"));
9
+ const readline_1 = __importDefault(require("readline"));
10
+ const child_process_1 = require("child_process");
14
11
  const CommitManager_1 = __importDefault(require("./CommitManager"));
15
- const ChunkManager_1 = __importDefault(require("./ChunkManager"));
16
- const BlockManager_1 = __importDefault(require("./BlockManager"));
17
12
  const ReviewManager_1 = __importDefault(require("./ReviewManager"));
18
- const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
19
13
  const string_utils_1 = require("../utils/string_utils");
20
- const config_utils_1 = require("../utils/config_utils");
21
14
  const prompt_utils_1 = require("../utils/prompt_utils");
15
+ const prompts_1 = require("../utils/prompts");
16
+ const StreamManager_1 = __importDefault(require("./StreamManager"));
22
17
  const specialCommands = [
23
18
  'save',
24
19
  'commit',
@@ -32,36 +27,27 @@ const specialCommands = [
32
27
  'help',
33
28
  'exit',
34
29
  ];
35
- class ChatManager {
30
+ class ChatManager extends StreamManager_1.default {
36
31
  /**
37
32
  * Creates an instance of ChatManager.
38
33
  * @param {ClovingGPTOptions} options - Configuration options for the ChatManager.
39
34
  */
40
35
  constructor(options) {
41
- this.options = options;
42
- this.chatHistory = [];
36
+ super(options);
43
37
  this.commandHistory = [];
44
38
  this.historyIndex = -1;
45
39
  this.multilineInput = '';
46
40
  this.isMultilineMode = false;
47
- this.contextFiles = {};
48
- this.prompt = '';
49
- this.isProcessing = false;
50
41
  this.isSilent = false;
51
- this.retryCount = 0;
52
- this.maxRetries = 3;
53
42
  this.isSilent = options.silent || false;
54
43
  options.stream = true;
55
44
  options.silent = true;
56
- this.gpt = new cloving_gpt_1.default(options);
57
45
  this.rl = readline_1.default.createInterface({
58
46
  input: process.stdin,
59
47
  output: process.stdout,
60
48
  prompt: colors_1.default.green.bold('cloving> '),
61
49
  historySize: 1000,
62
50
  });
63
- this.chunkManager = new ChunkManager_1.default();
64
- this.blockManager = new BlockManager_1.default();
65
51
  }
66
52
  /**
67
53
  * Initializes the ChatManager by loading context files, displaying a welcome message,
@@ -74,8 +60,7 @@ class ChatManager {
74
60
  console.log(`\nšŸ€ šŸ€ šŸ€ ${colors_1.default.bold('Welcome to Cloving REPL')} šŸ€ šŸ€ šŸ€\n`);
75
61
  this.displayAvailableCommands();
76
62
  console.log('\nWhat would you like to do?');
77
- this.rl.prompt();
78
- this.setupEventListeners();
63
+ this.setupReadline();
79
64
  }
80
65
  displayAvailableCommands() {
81
66
  console.log(`Type a freeform request or question to interact with your Cloving AI pair programmer.\n`);
@@ -93,69 +78,15 @@ class ChatManager {
93
78
  console.log(` - ${colors_1.default.yellow(colors_1.default.bold('exit'))} Quit this session`);
94
79
  }
95
80
  /**
96
- * Checks for the latest version of the app from GitHub and compares it with the current version.
97
- * If a new version is available, it notifies the user.
81
+ * Sets up readline and event listeners for the readline interface and process input.
98
82
  * @private
99
- * @returns {Promise<void>}
100
83
  */
101
- async checkForLatestVersion() {
102
- try {
103
- const response = await axios_1.default.get('https://api.github.com/repos/cloving-ai/cloving-cli/releases/latest');
104
- const latestVersion = response.data.tag_name;
105
- const currentVersion = `v${(0, prompt_utils_1.getPackageVersion)()}`;
106
- if (latestVersion !== currentVersion) {
107
- console.log(colors_1.default.bgWhite.black(`\n šŸš€ A new version of Cloving is available: ${latestVersion} `));
108
- console.log(colors_1.default.bgWhite.black(` You are currently using version: ${currentVersion} `));
109
- console.log(colors_1.default.bgWhite.black(` To upgrade, run: ${colors_1.default.bgWhite.black.bold('npm install -g cloving@latest')} `));
110
- }
111
- }
112
- catch (error) {
113
- // ignore errors
114
- }
115
- }
116
- /**
117
- * Sets up event listeners for the readline interface and process input.
118
- * @private
119
- */
120
- setupEventListeners() {
84
+ setupReadline() {
85
+ this.rl.prompt();
121
86
  this.rl.on('line', this.handleLine.bind(this));
122
87
  this.rl.on('close', this.handleClose.bind(this));
123
88
  process.stdin.on('keypress', this.handleKeypress.bind(this));
124
89
  }
125
- /**
126
- * Loads context files specified in the options or defaults to the primary language's directory.
127
- * @private
128
- * @returns {Promise<void>}
129
- */
130
- async loadContextFiles() {
131
- const config = (0, config_utils_1.getClovingConfig)();
132
- const primaryLanguage = config.languages.find((lang) => lang.primary);
133
- const defaultDirectory = primaryLanguage ? primaryLanguage.directory : '.';
134
- const testingDirectories = config.testingFrameworks?.map((framework) => framework.directory) || [];
135
- const testingDirectory = testingDirectories[0];
136
- let files = this.options.files || [defaultDirectory, testingDirectory].filter(Boolean);
137
- if (files.length > 0) {
138
- console.log(`\nBuilding chat session context...\n`);
139
- }
140
- for (const file of files) {
141
- // Skip if the file is not a string
142
- if (!file)
143
- continue;
144
- const previousCount = Object.keys(this.contextFiles).length;
145
- this.contextFiles = await (0, prompt_utils_1.addFileOrDirectoryToContext)(file, this.contextFiles, this.options);
146
- const newCount = Object.keys(this.contextFiles).length;
147
- const addedCount = newCount - previousCount;
148
- const totalTokens = this.calculateTotalTokens();
149
- console.log(colors_1.default.cyan(`šŸ“ Loaded context from: ${colors_1.default.bold(file)}`));
150
- console.log(colors_1.default.green(` āœ… Added ${addedCount} file(s) to context`));
151
- console.log(colors_1.default.yellow(` šŸ“Š Total tokens in context: ${totalTokens.toLocaleString()}\n`));
152
- }
153
- }
154
- calculateTotalTokens() {
155
- return Object.values(this.contextFiles).reduce((total, content) => {
156
- return total + Math.ceil(content.length / 4);
157
- }, 0);
158
- }
159
90
  /**
160
91
  * Handles each line of input from the user.
161
92
  * @private
@@ -365,23 +296,6 @@ class ChatManager {
365
296
  }
366
297
  this.rl.prompt();
367
298
  }
368
- async refreshContext() {
369
- await this.reloadContextFiles();
370
- const updatedSystemPrompt = (0, prompt_utils_1.generateCodegenPrompt)(this.contextFiles);
371
- this.chatHistory[0] = { role: 'user', content: updatedSystemPrompt };
372
- this.chatHistory[1] = { role: 'assistant', content: 'What would you like to do?' };
373
- }
374
- async reloadContextFiles() {
375
- for (const filePath in this.contextFiles) {
376
- try {
377
- const content = await fs_1.default.promises.readFile(path_1.default.resolve(filePath), 'utf8');
378
- this.contextFiles[filePath] = content;
379
- }
380
- catch (error) {
381
- console.error(`Error reading file ${filePath}:`, error);
382
- }
383
- }
384
- }
385
299
  isRemoveCommand(command) {
386
300
  return command.startsWith('rm ');
387
301
  }
@@ -458,14 +372,14 @@ class ChatManager {
458
372
  async handleReview() {
459
373
  this.options.stream = false;
460
374
  const reviewManager = new ReviewManager_1.default(this.options);
461
- await reviewManager.review();
375
+ await reviewManager.generateReview();
462
376
  this.options.stream = true;
463
377
  this.rl.prompt();
464
378
  }
465
379
  async handleCommit() {
466
380
  this.options.stream = false;
467
381
  const commitManager = new CommitManager_1.default(this.options);
468
- await commitManager.commit();
382
+ await commitManager.generateCommit();
469
383
  this.options.stream = true;
470
384
  this.rl.prompt();
471
385
  }
@@ -521,11 +435,9 @@ class ChatManager {
521
435
  return;
522
436
  }
523
437
  this.isProcessing = true;
524
- this.chunkManager = new ChunkManager_1.default();
525
438
  try {
526
439
  await this.refreshContext();
527
- this.prompt = this.generatePrompt(input);
528
- this.addChatHistory(this.prompt);
440
+ this.addUserPrompt(this.generatePrompt(input));
529
441
  if (!this.isSilent) {
530
442
  const fullPrompt = this.chatHistory
531
443
  .map((m) => {
@@ -580,120 +492,23 @@ class ChatManager {
580
492
  prompt: this.prompt,
581
493
  messages: this.chatHistory,
582
494
  });
583
- let accumulatedContent = '';
584
- this.handleResponseStream(responseStream, accumulatedContent);
495
+ this.handleResponseStream(responseStream);
585
496
  }
586
497
  catch (err) {
587
498
  this.handleError(err);
588
499
  }
589
500
  }
590
- addChatHistory(input) {
591
- this.chatHistory.push({ role: 'user', content: input });
592
- }
593
- /**
594
- * Handles the streaming response from the AI model.
595
- * This method processes the incoming data chunks, converts them to readable output,
596
- * and manages the accumulated content.
597
- *
598
- * @param responseStream - The stream of response data from the AI model.
599
- * @param accumulatedContent - A string to accumulate the complete response content.
600
- */
601
- handleResponseStream(responseStream, accumulatedContent) {
602
- let animationInterval;
603
- this.blockManager = new BlockManager_1.default();
604
- this.chunkManager = new ChunkManager_1.default();
605
- this.blockManager.on('startGeneratingCode', () => {
606
- process.stdout.write(colors_1.default.gray.bold('\nGenerating code... '));
607
- let animationIndex = 0;
608
- const animationChars = ['/', '|', '\\', '-'];
609
- animationInterval = setInterval(() => {
610
- process.stdout.write('\b'.repeat(1));
611
- process.stdout.write(colors_1.default.gray.bold(animationChars[animationIndex]));
612
- animationIndex = (animationIndex + 1) % animationChars.length;
613
- }, 100);
614
- });
615
- this.blockManager.on('endGeneratingCode', () => {
616
- clearInterval(animationInterval);
617
- process.stdout.write('\b'.repeat(100));
618
- });
619
- this.blockManager.on('content', (content) => {
620
- process.stdout.write(content);
621
- accumulatedContent += content;
622
- });
623
- this.blockManager.on('codeBlock', (codeBlock) => {
624
- const { currentNewBlock } = codeBlock;
625
- if (currentNewBlock?.currentContent && currentNewBlock?.newContent) {
626
- // Current/New format
627
- process.stdout.write(colors_1.default.gray.bold(`\`\`\`${currentNewBlock.language} \n`));
628
- process.stdout.write(colors_1.default.gray.bold(`<<<<<<< CURRENT ${currentNewBlock.filePath}\n`));
629
- process.stdout.write((0, cli_highlight_1.default)(currentNewBlock.currentContent, { language: currentNewBlock.language }));
630
- process.stdout.write(colors_1.default.gray.bold('\n=======\n'));
631
- process.stdout.write((0, cli_highlight_1.default)(currentNewBlock.newContent, { language: currentNewBlock.language }));
632
- process.stdout.write(colors_1.default.gray.bold('\n>>>>>>> NEW\n```'));
633
- }
634
- else {
635
- if (codeBlock.raw) {
636
- // Raw format
637
- process.stdout.write(` \n`);
638
- process.stdout.write((0, cli_highlight_1.default)(codeBlock.raw));
639
- }
640
- else {
641
- process.stdout.write(` \n`);
642
- }
643
- }
644
- accumulatedContent += codeBlock.raw;
645
- });
646
- // Handle content chunks from the ChunkManager
647
- this.chunkManager.on('content', (buffer) => {
648
- let convertedStream = this.gpt.convertStream(buffer);
649
- while (convertedStream !== null) {
650
- const { output, lastChar } = convertedStream;
651
- // buffer the output to the BlockManager
652
- this.blockManager.addContent(output);
653
- // clear the buffer from the ChunkManager
654
- this.chunkManager.clearBuffer(lastChar);
655
- // Try to convert the rest of the buffer to a string
656
- buffer = buffer.slice(lastChar);
657
- convertedStream = this.gpt.convertStream(buffer);
658
- }
659
- });
660
- // Handle incoming data chunks from the response stream
661
- responseStream.data.on('data', (chunk) => {
662
- // Convert the chunk to a string and add it to the ChunkManager
663
- if (!chunk)
664
- return;
665
- // a chunk is a JSON object with different keys depending on the AI service provider
666
- const chunkString = chunk.toString();
667
- this.chunkManager.addChunk(chunkString);
668
- });
669
- // Handle the end of the response stream
670
- responseStream.data.on('end', () => {
671
- // Finalize the response when the stream ends
672
- this.finalizeResponse(accumulatedContent);
673
- });
674
- // Handle any errors in the response stream
675
- responseStream.data.on('error', (error) => {
676
- console.error('Error streaming response:', error);
677
- this.isProcessing = false;
678
- process.stdout.write('\n');
679
- this.rl.prompt();
680
- });
681
- }
682
- async finalizeResponse(accumulatedContent) {
683
- this.chatHistory.push({ role: 'assistant', content: accumulatedContent.trim() });
501
+ async finalizeResponse() {
502
+ this.addAssistantResponse(this.responseString);
684
503
  this.isProcessing = false;
685
- if (accumulatedContent) {
686
- const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(accumulatedContent);
504
+ if (this.responseString !== '') {
505
+ const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(this.responseString);
687
506
  const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
688
507
  if (!canApply) {
689
508
  if (this.retryCount < this.maxRetries) {
690
509
  console.log(`\n\n${summary}\n\n${colors_1.default.yellow.bold('WARNING')} Some of the provided code blocks could not be automatically applied. Retrying (Attempt ${this.retryCount + 1}/${this.maxRetries})...\n`);
691
510
  this.retryCount++;
692
- this.processUserInput(`Some of the provided code blocks could not be applied,
693
- please match the existing code with a few more lines of context and make sure it is a character for character exact match.
694
- Never use "rest of the existing code" or "rest of the code" in your response, instead break the code block into smaller blocks.
695
-
696
- ${summary}`);
511
+ this.processUserInput(`${prompts_1.CODEGEN_COULDNT_APPLY}\n\n${summary}`);
697
512
  return;
698
513
  }
699
514
  else {
@@ -720,8 +535,7 @@ You can follow up with another request or:
720
535
  `);
721
536
  this.rl.prompt();
722
537
  }
723
- handleError(err) {
724
- const error = err;
538
+ handleError(error) {
725
539
  let errorMessage = error.message || 'An error occurred.';
726
540
  const errorNumber = error.response?.status || 'unknown';
727
541
  switch (errorNumber) {
@@ -738,7 +552,6 @@ You can follow up with another request or:
738
552
  errorMessage = 'Internal server error';
739
553
  break;
740
554
  }
741
- const promptTokens = Math.ceil(this.prompt.length / 4).toLocaleString();
742
555
  const statusMessage = error.response?.data?.statusMessage;
743
556
  console.error(colors_1.default.red(`\nError (${errorNumber}) while submitting the prompt to the AI API\n\n${statusMessage}\n`));
744
557
  this.isProcessing = false;
@@ -774,7 +587,8 @@ ${prompt}
774
587
 
775
588
  ### Note
776
589
 
777
- Whenever possible, break up the changes into pieces and make sure every change is in its own CURRENT / NEW block.`;
590
+ Whenever possible, break up the changes into pieces and make sure every change is in its own CURRENT / NEW block.
591
+ 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.`;
778
592
  }
779
593
  handleClose() {
780
594
  console.log('Goodbye!');
@@ -5,14 +5,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const prompts_1 = require("@inquirer/prompts");
7
7
  const cli_highlight_1 = __importDefault(require("cli-highlight"));
8
+ const colors_1 = __importDefault(require("colors"));
8
9
  const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
10
+ const config_utils_1 = require("../utils/config_utils");
9
11
  const prompt_utils_1 = require("../utils/prompt_utils");
10
12
  const string_utils_1 = require("../utils/string_utils");
11
- class CodeManager {
13
+ const prompts_2 = require("../utils/prompts");
14
+ const StreamManager_1 = __importDefault(require("./StreamManager"));
15
+ class CodeManager extends StreamManager_1.default {
12
16
  constructor(options) {
13
- this.options = options;
14
- this.contextFiles = {};
15
- this.chatHistory = [];
17
+ super(options);
18
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
19
+ options.stream = true;
16
20
  this.gpt = new cloving_gpt_1.default(options);
17
21
  }
18
22
  async initialize() {
@@ -26,7 +30,7 @@ class CodeManager {
26
30
  let includeMoreFiles = true;
27
31
  while (includeMoreFiles) {
28
32
  const contextFile = await (0, prompts_1.input)({
29
- message: `Enter the relative path of a file or directory you would like to include as context (or press enter to continue):`,
33
+ message: `Enter the relative path of a file or directory you would like to include as context (or press enter to continue): `,
30
34
  });
31
35
  if (contextFile) {
32
36
  this.contextFiles = await (0, prompt_utils_1.addFileOrDirectoryToContext)(contextFile, this.contextFiles, this.options);
@@ -42,26 +46,7 @@ class CodeManager {
42
46
  });
43
47
  this.options.prompt = userPrompt;
44
48
  }
45
- let response = await this.generateCode(this.options.prompt);
46
- const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(response);
47
- const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
48
- if (!canApply) {
49
- console.log('The generated code could not be automatically applied. It will try again asking for more details.');
50
- console.log(`Some of the provided code blocks could not be applied,
51
- please match the existing code with a few more lines of context and make sure it is a character for character exact match.
52
-
53
- ${summary}`);
54
- response = await this.generateCode(this.options.prompt);
55
- this.displayGeneratedCode(response);
56
- }
57
- this.displayGeneratedCode(response);
58
- if (this.options.save) {
59
- const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(response);
60
- await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
61
- }
62
- else {
63
- await this.handleUserAction(response, this.options.prompt);
64
- }
49
+ await this.generateCode(this.options.prompt);
65
50
  }
66
51
  catch (err) {
67
52
  const error = err;
@@ -69,35 +54,26 @@ ${summary}`);
69
54
  }
70
55
  }
71
56
  async generateCode(userPrompt) {
57
+ await this.checkForLatestVersion();
58
+ await this.loadContextFiles();
72
59
  if (this.chatHistory.length === 0) {
73
- const systemPrompt = (0, prompt_utils_1.generateCodegenPrompt)(this.contextFiles);
74
- this.chatHistory.push({ role: 'user', content: systemPrompt });
75
- this.chatHistory.push({ role: 'assistant', content: 'What would you like to do?' });
60
+ this.addUserPrompt((0, prompt_utils_1.generateCodegenPrompt)(this.contextFiles));
61
+ this.addAssistantResponse('What would you like to do?');
62
+ }
63
+ this.addUserPrompt(userPrompt);
64
+ try {
65
+ const responseStream = await this.gpt.streamText({
66
+ prompt: userPrompt,
67
+ messages: this.chatHistory,
68
+ });
69
+ this.handleResponseStream(responseStream);
70
+ }
71
+ catch (err) {
72
+ const error = err;
73
+ console.error('Error streaming response:', error.message);
76
74
  }
77
- this.chatHistory.push({ role: 'user', content: userPrompt });
78
- return await this.gpt.generateText({ prompt: userPrompt, messages: this.chatHistory });
79
- }
80
- displayGeneratedCode(rawCodeCommand) {
81
- (0, string_utils_1.parseMarkdownInstructions)(rawCodeCommand).map((code) => {
82
- if (code.trim().startsWith('```')) {
83
- const lines = code.split('\n');
84
- const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
85
- console.log(lines[0]);
86
- try {
87
- console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
88
- }
89
- catch (error) {
90
- // don't highlight if it fails
91
- console.log(lines.slice(1, -1).join('\n'));
92
- }
93
- console.log(lines.slice(-1)[0]);
94
- }
95
- else {
96
- console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
97
- }
98
- });
99
75
  }
100
- async handleUserAction(response, prompt) {
76
+ async handleUserAction(response) {
101
77
  const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(response);
102
78
  const files = Array.from(new Set(currentNewBlocks.map((block) => block.filePath)));
103
79
  if (this.options.save) {
@@ -133,9 +109,7 @@ ${summary}`);
133
109
  includeMoreFiles = false;
134
110
  }
135
111
  }
136
- const newResponse = await this.generateCode(newPrompt);
137
- this.displayGeneratedCode(newResponse);
138
- await this.handleUserAction(newResponse, newPrompt);
112
+ await this.generateCode(newPrompt);
139
113
  break;
140
114
  case 'explain':
141
115
  const explainPrompt = this.generateExplainCodePrompt(response);
@@ -163,7 +137,7 @@ ${summary}`);
163
137
  break;
164
138
  case 'saveAll':
165
139
  await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
166
- console.log('All files have been saved.');
140
+ console.log(colors_1.default.green('\nAll files have been saved.'));
167
141
  break;
168
142
  case 'done':
169
143
  break;
@@ -176,5 +150,34 @@ ${summary}`);
176
150
 
177
151
  Please briefly explain how the code works in this.`;
178
152
  }
153
+ /**
154
+ * Finalizes the response processing.
155
+ * This method should be overridden to provide specific finalization behavior.
156
+ * @protected
157
+ */
158
+ async finalizeResponse() {
159
+ this.addAssistantResponse(this.responseString);
160
+ this.isProcessing = false;
161
+ if (this.responseString !== '') {
162
+ const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(this.responseString);
163
+ const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
164
+ if (!canApply) {
165
+ if (this.retryCount < this.maxRetries) {
166
+ console.log(`\n\n${summary}\n\n${colors_1.default.yellow.bold('WARNING')} Some of the provided code blocks could not be automatically applied. Retrying (Attempt ${this.retryCount + 1}/${this.maxRetries})...\n`);
167
+ this.retryCount++;
168
+ this.generateCode(`${prompts_2.CODEGEN_COULDNT_APPLY}\n\n${summary}`);
169
+ return;
170
+ }
171
+ else {
172
+ console.log(`\n\n${colors_1.default.red.bold('ERROR')} Failed to generate a diff that could be cleanly applied after ${this.maxRetries} attempts. Please review the changes manually and try again.\n`);
173
+ this.retryCount = 0; // Reset the retry count for future attempts
174
+ }
175
+ }
176
+ else {
177
+ this.retryCount = 0; // Reset the retry count on successful application
178
+ }
179
+ }
180
+ this.handleUserAction(this.responseString);
181
+ }
179
182
  }
180
183
  exports.default = CodeManager;
@@ -15,7 +15,7 @@ class CommitManager {
15
15
  options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
16
16
  this.gpt = new cloving_gpt_1.default(options);
17
17
  }
18
- async commit() {
18
+ async generateCommit() {
19
19
  try {
20
20
  // Get the git diff
21
21
  const diff = (0, child_process_1.execSync)('git diff HEAD').toString().trim();
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const prompts_1 = require("@inquirer/prompts");
7
+ const colors_1 = __importDefault(require("colors"));
8
+ const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
9
+ const config_utils_1 = require("../utils/config_utils");
10
+ const prompts_2 = require("../utils/prompts");
11
+ const prompt_utils_1 = require("../utils/prompt_utils");
12
+ const string_utils_1 = require("../utils/string_utils");
13
+ const StreamManager_1 = __importDefault(require("./StreamManager"));
14
+ /**
15
+ * Manages the generation of documentation for specified files.
16
+ */
17
+ class DocsManager extends StreamManager_1.default {
18
+ /**
19
+ * Creates a new DocumentationManager instance.
20
+ * @param {ClovingGPTOptions} options - Configuration options for the DocumentationManager.
21
+ */
22
+ constructor(options) {
23
+ super(options);
24
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
25
+ options.stream = true;
26
+ this.gpt = new cloving_gpt_1.default(options);
27
+ }
28
+ /**
29
+ * Generates documentation for the specified files.
30
+ * @returns {Promise<void>}
31
+ */
32
+ async generateDocs(extraPrompt) {
33
+ try {
34
+ this.isProcessing = true;
35
+ if (this.retryCount === 0) {
36
+ await this.checkForLatestVersion();
37
+ }
38
+ await this.loadContextFiles();
39
+ this.addUserPrompt(`${(0, prompt_utils_1.generateDocsPrompt)(this.contextFiles)}\n\n${extraPrompt || ''}`);
40
+ const responseStream = await this.gpt.streamText({
41
+ prompt: this.prompt,
42
+ messages: this.chatHistory,
43
+ });
44
+ this.handleResponseStream(responseStream);
45
+ }
46
+ catch (error) {
47
+ console.error(colors_1.default.red('Error generating documentation:'), error);
48
+ }
49
+ }
50
+ /**
51
+ * Finalizes the response processing.
52
+ * This method should be overridden to provide specific finalization behavior.
53
+ * @protected
54
+ */
55
+ async finalizeResponse() {
56
+ this.addAssistantResponse(this.responseString);
57
+ this.isProcessing = false;
58
+ if (this.responseString !== '') {
59
+ const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(this.responseString);
60
+ const [canApply, summary] = await (0, string_utils_1.checkBlocksApplicability)(currentNewBlocks);
61
+ if (canApply && currentNewBlocks.length > 0) {
62
+ const shouldSave = await (0, prompts_1.confirm)({
63
+ message: '\n\nDo you want to save the generated documentation?',
64
+ default: true,
65
+ });
66
+ if (shouldSave) {
67
+ await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
68
+ console.log(colors_1.default.green('\nDocumentation has been saved to the respective files.'));
69
+ }
70
+ }
71
+ else {
72
+ if (this.retryCount < this.maxRetries) {
73
+ console.log(`\n\n${summary}\n\n${colors_1.default.yellow.bold('WARNING')} Some of the provided code blocks could not be automatically applied. Retrying (Attempt ${this.retryCount + 1}/${this.maxRetries})...\n`);
74
+ this.retryCount++;
75
+ this.generateDocs(`\n\n${prompts_2.CODEGEN_COULDNT_APPLY}\n\n${summary}`);
76
+ return;
77
+ }
78
+ else {
79
+ console.log(`\n\n${colors_1.default.red.bold('ERROR')} Failed to generate a diff that could be cleanly applied after ${this.maxRetries} attempts. Please review the changes manually and try again.\n`);
80
+ this.retryCount = 0; // Reset the retry count for future attempts
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+ exports.default = DocsManager;