cloving 0.3.2 → 0.3.3

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/dist/index.js CHANGED
@@ -5,8 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const commander_1 = require("commander");
8
- const fs_1 = require("fs");
9
- const path_1 = require("path");
10
8
  const config_1 = __importDefault(require("./commands/config"));
11
9
  const init_1 = __importDefault(require("./commands/init"));
12
10
  const proxy_1 = __importDefault(require("./commands/proxy"));
@@ -19,17 +17,12 @@ const commit_1 = __importDefault(require("./commands/generate/commit"));
19
17
  const context_1 = __importDefault(require("./commands/generate/context"));
20
18
  const unit_tests_1 = __importDefault(require("./commands/generate/unit_tests"));
21
19
  const review_1 = __importDefault(require("./commands/generate/review"));
22
- // Function to get version from package.json
23
- const getPackageVersion = () => {
24
- const packagePath = (0, path_1.join)(__dirname, 'package.json');
25
- const packageJson = JSON.parse((0, fs_1.readFileSync)(packagePath, 'utf8'));
26
- return packageJson.version;
27
- };
20
+ const command_utils_1 = require("./utils/command_utils");
28
21
  const program = new commander_1.Command();
29
22
  program
30
23
  .name('cloving')
31
24
  .description('Integrate AI into your development workflow for generating commit messages, code reviews, and unit tests.')
32
- .version(getPackageVersion());
25
+ .version((0, command_utils_1.getPackageVersion)());
33
26
  program
34
27
  .command('config')
35
28
  .description('Configure cloving with your API key and models to use')
@@ -7,6 +7,8 @@ const copy_paste_1 = __importDefault(require("copy-paste"));
7
7
  const readline_1 = __importDefault(require("readline"));
8
8
  const child_process_1 = require("child_process");
9
9
  const colors_1 = __importDefault(require("colors"));
10
+ const axios_1 = __importDefault(require("axios"));
11
+ const cli_highlight_1 = __importDefault(require("cli-highlight"));
10
12
  const CommitManager_1 = __importDefault(require("./CommitManager"));
11
13
  const ChunkManager_1 = __importDefault(require("./ChunkManager"));
12
14
  const ReviewManager_1 = __importDefault(require("./ReviewManager"));
@@ -42,6 +44,8 @@ class ChatManager {
42
44
  this.contextFiles = {};
43
45
  this.prompt = '';
44
46
  this.isProcessing = false;
47
+ this.isSilent = false;
48
+ this.isSilent = options.silent || false;
45
49
  options.stream = true;
46
50
  options.silent = true;
47
51
  this.gpt = new cloving_gpt_1.default(options);
@@ -59,6 +63,7 @@ class ChatManager {
59
63
  * @returns {Promise<void>}
60
64
  */
61
65
  async initialize() {
66
+ await this.checkForLatestVersion();
62
67
  await this.loadContextFiles();
63
68
  console.log(`\nšŸ€ šŸ€ šŸ€ ${colors_1.default.bold('Welcome to Cloving REPL')} šŸ€ šŸ€ šŸ€\n`);
64
69
  this.displayAvailableCommands();
@@ -81,6 +86,27 @@ class ChatManager {
81
86
  console.log(` - ${colors_1.default.yellow(colors_1.default.bold('help'))} Display this help message`);
82
87
  console.log(` - ${colors_1.default.yellow(colors_1.default.bold('exit'))} Quit this session`);
83
88
  }
89
+ /**
90
+ * Checks for the latest version of the app from GitHub and compares it with the current version.
91
+ * If a new version is available, it notifies the user.
92
+ * @private
93
+ * @returns {Promise<void>}
94
+ */
95
+ async checkForLatestVersion() {
96
+ try {
97
+ const response = await axios_1.default.get('https://api.github.com/repos/cloving-ai/cloving-cli/releases/latest');
98
+ const latestVersion = response.data.tag_name;
99
+ const currentVersion = `v${(0, command_utils_1.getPackageVersion)()}`;
100
+ if (latestVersion !== currentVersion) {
101
+ console.log(colors_1.default.bgWhite.black(`\n šŸš€ A new version of Cloving is available: ${latestVersion} `));
102
+ console.log(colors_1.default.bgWhite.black(` You are currently using version: ${currentVersion} `));
103
+ console.log(colors_1.default.bgWhite.black(` To upgrade, run: ${colors_1.default.bgWhite.black.bold('npm install -g cloving@latest')} `));
104
+ }
105
+ }
106
+ catch (error) {
107
+ // ignore errors
108
+ }
109
+ }
84
110
  /**
85
111
  * Sets up event listeners for the readline interface and process input.
86
112
  * @private
@@ -313,11 +339,7 @@ class ChatManager {
313
339
  try {
314
340
  this.contextFiles = await (0, command_utils_1.addFileOrDirectoryToContext)(filePath, this.contextFiles, this.options);
315
341
  console.log(`\nAdded ${colors_1.default.bold(colors_1.default.green(filePath))} to this chat session's context`);
316
- // Update the chat history with the new context
317
- const updatedSystemPrompt = (0, command_utils_1.generateCodegenPrompt)(this.contextFiles);
318
- this.chatHistory[0] = { role: 'user', content: updatedSystemPrompt };
319
- // Log the update
320
- console.log(colors_1.default.yellow(`Updated chat context with new file(s)`));
342
+ this.refreshContext();
321
343
  }
322
344
  catch (error) {
323
345
  console.error(`Failed to add ${colors_1.default.bold(colors_1.default.red(filePath))} to this chat session's context:`, error);
@@ -331,6 +353,7 @@ class ChatManager {
331
353
  refreshContext() {
332
354
  const updatedSystemPrompt = (0, command_utils_1.generateCodegenPrompt)(this.contextFiles);
333
355
  this.chatHistory[0] = { role: 'user', content: updatedSystemPrompt };
356
+ this.chatHistory[1] = { role: 'assistant', content: 'What would you like to do?' };
334
357
  }
335
358
  isRemoveCommand(command) {
336
359
  return command.startsWith('rm ');
@@ -470,9 +493,50 @@ class ChatManager {
470
493
  this.isProcessing = true;
471
494
  this.chunkManager = new ChunkManager_1.default();
472
495
  try {
473
- this.refreshContext(); // make sure the context is up-to-date
474
- this.initializeChatHistory(input);
496
+ this.refreshContext();
475
497
  this.prompt = this.generatePrompt(input);
498
+ this.addChatHistory(this.prompt);
499
+ if (!this.isSilent) {
500
+ const fullPrompt = this.chatHistory.map(m => {
501
+ const content = m.content.split('\n').map(line => {
502
+ if (line.startsWith('###')) {
503
+ return colors_1.default.red.bold(line);
504
+ }
505
+ else if (line.startsWith('##')) {
506
+ return colors_1.default.yellow.bold(line);
507
+ }
508
+ return line;
509
+ }).join('\n');
510
+ return m.role === 'user' ? `${colors_1.default.yellow.bold('## User Prompt')}\n\n${content}` : `${colors_1.default.green.bold('## šŸ€ Cloving\'s Response šŸ€')}\n\n${content}`;
511
+ }).join('\n\n');
512
+ // Review the prompt using the REPL
513
+ const tokenCount = Math.ceil(fullPrompt.length / 4).toLocaleString();
514
+ console.log(`The generated prompt is approximately ${tokenCount} tokens long. Would you like to review the prompt before sending it? ${colors_1.default.gray('(Y/n)')}`);
515
+ const reviewPrompt = await new Promise((resolve) => {
516
+ this.rl.question(colors_1.default.green.bold('cloving> '), (answer) => {
517
+ resolve(answer.trim().toLowerCase());
518
+ });
519
+ });
520
+ if (reviewPrompt === 'y' || reviewPrompt === '') {
521
+ console.log(colors_1.default.gray.bold('\n---------------------- PROMPT START ----------------------\n'));
522
+ const promptParts = fullPrompt.split('\n```');
523
+ const highlightedPrompt = promptParts.map((part, index) => index % 2 === 1 ? (0, cli_highlight_1.default)(part, { language: 'typescript' }) : part).join('\n```');
524
+ console.log(highlightedPrompt);
525
+ console.log(colors_1.default.gray.bold('\n---------------------- PROMPT END ----------------------\n'));
526
+ console.log(`Do you want to proceed with this prompt? ${colors_1.default.gray('(Y/n)')}`);
527
+ const confirmPrompt = await new Promise((resolve) => {
528
+ this.rl.question(colors_1.default.green.bold('cloving> '), (answer) => {
529
+ resolve(answer.trim().toLowerCase());
530
+ });
531
+ });
532
+ if (confirmPrompt !== 'y' && confirmPrompt !== '') {
533
+ console.log('Operation cancelled by user.');
534
+ this.isProcessing = false;
535
+ this.rl.prompt();
536
+ return;
537
+ }
538
+ }
539
+ }
476
540
  const responseStream = await this.gpt.streamText({ prompt: this.prompt, messages: this.chatHistory });
477
541
  let accumulatedContent = '';
478
542
  this.handleResponseStream(responseStream, accumulatedContent);
@@ -481,12 +545,7 @@ class ChatManager {
481
545
  this.handleError(err);
482
546
  }
483
547
  }
484
- initializeChatHistory(input) {
485
- if (this.chatHistory.length === 0) {
486
- const systemPrompt = (0, command_utils_1.generateCodegenPrompt)(this.contextFiles);
487
- this.chatHistory.push({ role: 'user', content: systemPrompt });
488
- this.chatHistory.push({ role: 'assistant', content: 'What would you like to do?' });
489
- }
548
+ addChatHistory(input) {
490
549
  this.chatHistory.push({ role: 'user', content: input });
491
550
  }
492
551
  handleResponseStream(responseStream, accumulatedContent) {
@@ -520,16 +579,16 @@ class ChatManager {
520
579
  this.isProcessing = false;
521
580
  process.stdout.write(`
522
581
 
523
- You can follow up with another request or:
524
- - type ${colors_1.default.yellow(colors_1.default.bold('"save"'))} to save all the changes to files
525
- - type ${colors_1.default.yellow(colors_1.default.bold('"commit"'))} to commit the changes to git with a AI-generated message
526
- - type ${colors_1.default.yellow(colors_1.default.bold('"copy"'))} to copy the last response to clipboard
527
- - type ${colors_1.default.yellow(colors_1.default.bold('"review"'))} to start a code review
528
- - type ${colors_1.default.yellow(colors_1.default.bold('"add <file-path>"'))} to add a file to the chat context
529
- - type ${colors_1.default.yellow(colors_1.default.bold('"rm <pattern>"'))} to remove files from the chat context
530
- - type ${colors_1.default.yellow(colors_1.default.bold('"ls <pattern>"'))} to list files in the chat context
531
- - type ${colors_1.default.yellow(colors_1.default.bold('"git <command>"'))} to run a git command
532
- - type ${colors_1.default.yellow(colors_1.default.bold('"exit"'))} to quit this session
582
+ You can follow up with another request or:
583
+ - type ${colors_1.default.yellow(colors_1.default.bold('"save"'))} to save all the changes to files
584
+ - type ${colors_1.default.yellow(colors_1.default.bold('"commit"'))} to commit the changes to git with a AI - generated message
585
+ - type ${colors_1.default.yellow(colors_1.default.bold('"copy"'))} to copy the last response to clipboard
586
+ - type ${colors_1.default.yellow(colors_1.default.bold('"review"'))} to start a code review
587
+ - type ${colors_1.default.yellow(colors_1.default.bold('"add <file-path>"'))} to add a file to the chat context
588
+ - type ${colors_1.default.yellow(colors_1.default.bold('"rm <pattern>"'))} to remove files from the chat context
589
+ - type ${colors_1.default.yellow(colors_1.default.bold('"ls <pattern>"'))} to list files in the chat context
590
+ - type ${colors_1.default.yellow(colors_1.default.bold('"git <command>"'))} to run a git command
591
+ - type ${colors_1.default.yellow(colors_1.default.bold('"exit"'))} to quit this session
533
592
  `);
534
593
  this.rl.prompt();
535
594
  }
@@ -552,7 +611,7 @@ class ChatManager {
552
611
  break;
553
612
  }
554
613
  const promptTokens = Math.ceil(this.prompt.length / 4).toLocaleString();
555
- console.error(`Error processing a ${promptTokens} token prompt:`, errorMessage, `(${errorNumber})\n`);
614
+ console.error(`Error processing a ${promptTokens} token prompt: `, errorMessage, `(${errorNumber}) \n`);
556
615
  this.isProcessing = false;
557
616
  this.rl.prompt();
558
617
  }
@@ -580,27 +639,13 @@ class ChatManager {
580
639
  * accurate and relevant responses.
581
640
  */
582
641
  generatePrompt(prompt) {
583
- const contextFileContents = Object.keys(this.contextFiles)
584
- .map((file) => `### Contents of ${file}\n\n${this.contextFiles[file]}\n\n`)
585
- .join('\n');
586
- const allButLast = this.chatHistory.slice(0, -1).map(msg => `${msg.role.toUpperCase()}: ${msg.content}`).join('\n\n');
587
642
  return `### Request
588
643
 
589
644
  ${prompt}
590
645
 
591
- ${contextFileContents}
592
-
593
- ### Full Chat History Context
594
-
595
- ${allButLast}
596
-
597
- ### Current Request
598
-
599
- ${prompt}
600
-
601
646
  ### Note
602
647
 
603
- Whenever possible, break up the changes into pieces and make sure every change is in its own CURRENT/NEW block.`;
648
+ Whenever possible, break up the changes into pieces and make sure every change is in its own CURRENT / NEW block.`;
604
649
  }
605
650
  handleClose() {
606
651
  console.log('Goodbye!');
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "packageManager": "yarn@1.22.22",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -3,20 +3,37 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.addFileOrDirectoryToContext = exports.readFileContent = exports.fetchModels = exports.runCommand = exports.generateFileList = exports.getAllFilesInDirectory = exports.checkForSpecialFiles = exports.collectSpecialFileContents = exports.generateShellPrompt = exports.generateCodegenPrompt = exports.SPECIAL_FILES = void 0;
6
+ exports.addFileOrDirectoryToContext = exports.readFileContent = exports.fetchModels = exports.runCommand = exports.generateFileList = exports.getAllFilesInDirectory = exports.checkForSpecialFiles = exports.collectSpecialFileContents = exports.generateShellPrompt = exports.generateCodegenPrompt = exports.getPackageVersion = exports.SPECIAL_FILES = void 0;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const child_process_1 = require("child_process");
10
10
  const isbinaryfile_1 = require("isbinaryfile");
11
11
  const colors_1 = __importDefault(require("colors"));
12
+ const path_2 = require("path");
12
13
  const config_utils_1 = require("./config_utils");
13
14
  exports.SPECIAL_FILES = [
14
15
  'package.json', 'Gemfile', 'requirements.txt', 'Pipfile', 'pyproject.toml', 'pom.xml', 'build.gradle',
15
16
  '.csproj', 'packages.config', 'composer.json', 'CMakeLists.txt', 'conanfile.txt', 'conanfile.py',
16
17
  'go.mod', 'Cargo.toml', 'Package.swift', 'build.gradle.kts', 'Podfile', 'Cartfile', 'cpanfile',
17
18
  'DESCRIPTION', 'mix.exs', 'build.sbt', 'pubspec.yaml', 'stack.yaml', 'cabal.project', 'Project.toml',
18
- 'rockspec', 'rebar.config', 'project.clj', 'tsconfig.json'
19
+ 'rockspec', 'rebar.config', 'project.clj', 'tsconfig.json', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yaml',
20
+ '.eslintrc.yml', '.eslintrc', '.prettierrc', '.prettierrc.js', '.prettierrc.json', '.prettierrc.yaml',
21
+ '.prettierrc.yml', '.prettierrc', '.stylelintrc', '.stylelintrc.json', '.stylelintrc.yaml', '.stylelintrc.yml',
22
+ '.stylelintrc', 'jest.config.js', 'jest.config.json', 'jest.config.ts', 'jest.config.cjs', 'jest.config.mjs',
23
+ 'webpack.config.js', 'webpack.config.ts', 'webpack.config.cjs', 'webpack.config.mjs', 'webpack.config.common.js',
19
24
  ];
25
+ const getPackageVersion = () => {
26
+ const packagePath = (0, path_2.join)(__dirname, '..', 'package.json');
27
+ const packageJson = JSON.parse(fs_1.default.readFileSync(packagePath, 'utf8'));
28
+ return packageJson.version;
29
+ };
30
+ exports.getPackageVersion = getPackageVersion;
31
+ const renderAsString = (contextFile) => {
32
+ if (typeof contextFile === 'string') {
33
+ return contextFile;
34
+ }
35
+ return JSON.stringify(contextFile, null, 2);
36
+ };
20
37
  const generateCodegenPrompt = (contextFilesContent) => {
21
38
  const instructions = `## AI Code Generation Instructions
22
39
 
@@ -52,13 +69,16 @@ const generateCodegenPrompt = (contextFilesContent) => {
52
69
  7. **Close the block with three backticks.**
53
70
  \`\`\``;
54
71
  const specialFileContents = (0, exports.collectSpecialFileContents)();
55
- const specialFiles = Object.keys(specialFileContents).map((file) => `### Contents of **${file}**\n\n\`\`\`\n${JSON.stringify(specialFileContents[file], null, 2)}\n\`\`\`\n\n`).join('\n');
72
+ // detect if specialFileContents[file] is a string or an object
73
+ const specialFiles = Object.keys(specialFileContents).map((file) => `### Contents of **${file}**\n\n\`\`\`\n${renderAsString(specialFileContents[file])}\n\`\`\`\n\n`).join('\n');
56
74
  const contextFileContents = Object.keys(contextFilesContent).map((file) => `### Contents of **${file}**\n\n\`\`\`\n${contextFilesContent[file]}\n\`\`\`\n\n`).join('\n');
57
75
  const prompt = `${instructions}
58
76
 
59
77
  ## Description of App
60
78
 
79
+ \`\`\`json
61
80
  ${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
81
+ \`\`\`
62
82
 
63
83
  ## Special Files
64
84
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "packageManager": "yarn@1.22.22",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",