From 825864216febfe7dcfd0bdab2ccbec812060d188 Mon Sep 17 00:00:00 2001 From: Jonathan Atiene <34762800+bemijonathan@users.noreply.github.com> Date: Sun, 18 Jun 2023 08:52:19 +0100 Subject: [PATCH] added: html editor to view file diff and show in a hapi server (#11) * added: html editor to view file diff and show in a hapi server * added server remove feedback.txt --- .gitignore | 1 + feedback-samples/feedback-code_review_3.txt | 489 +++++++++++ feedback-samples/feedback_code_review.txt | 500 ++++++++++++ feedback-samples/feedback_code_review_2.txt | 637 +++++++++++++++ feedback.txt | 270 ++++++ index.ts | 48 +- package-lock.json | 614 ++++++++++++++ package.json | 4 +- public/index.css | 185 +++++ public/index.html | 858 ++++++++++++++++++++ public/index.js | 59 ++ src/code-review.ts | 16 +- src/feedback-server.ts | 175 ++++ src/openai.ts | 41 +- src/utils.ts | 4 +- 15 files changed, 3868 insertions(+), 33 deletions(-) create mode 100644 feedback-samples/feedback-code_review_3.txt create mode 100644 feedback-samples/feedback_code_review.txt create mode 100644 feedback-samples/feedback_code_review_2.txt create mode 100644 feedback.txt create mode 100644 public/index.css create mode 100644 public/index.html create mode 100644 public/index.js create mode 100644 src/feedback-server.ts diff --git a/.gitignore b/.gitignore index 0977622..7ce23b6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules dist project/** .env +feedback.txt diff --git a/feedback-samples/feedback-code_review_3.txt b/feedback-samples/feedback-code_review_3.txt new file mode 100644 index 0000000..03a81cf --- /dev/null +++ b/feedback-samples/feedback-code_review_3.txt @@ -0,0 +1,489 @@ +-------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/index.ts + + + Criticism: Issues: +- Inconsistent naming conventions (e.g. FeedBackServer vs FeedbackServer) +- Unused variable (config) on line 18 +- Redundant sleep function call on line 15 +- Lack of error handling for codeReview and getScore functions +- No documentation or comments for functions and classes + +Recommendations: +- Consistently use camelCase naming convention for variables and classes +- Remove unused variable on line 18 +- Remove redundant sleep function call on line 15 +- Implement try-catch blocks for codeReview and getScore functions to handle errors +- Add documentation and comments for functions and classes to improve readability and maintainability + +Rewrite: +``` +#!/usr/bin/env node + +import { ConfigStore } from './src/config'; +import { FeedbackServer } from './src/feedback-server'; +import { AutoReviewer } from './src/index'; +import { Utils } from './src/utils'; + +const chalk = require('chalk'); + +/** + * Displays a welcome message to the user + */ +const showWelcomeMessage = () => { + const welcomeText = ` + ======================================================= + + Welcome to AutoReviewer - Your Code Review Helper + + ======================================================= + + 1. AutoReviewer will review your code and provide you with feedback + 2. AutoReviewer will also provide you with a score for your code review + + ------------------------------------------------------- + `; + Utils.log(chalk.greenBright.bold(welcomeText)); +} + +showWelcomeMessage(); + +const autoReviewer = new AutoReviewer(); +const feedbackServer = new FeedbackServer(); + +if (autoReviewer.openaiApiKey) { + (async () => { + try { + await autoReviewer.codeReview(); + const score = await autoReviewer.getScore(); + Utils.log(chalk.greenBright.bold('Code Review Completed successfully ☑️')); + Utils.log(chalk.greenBright.bold(`Your code score is: ${score} / 10`)); + Utils.log(chalk.greenBright.bold(`Please visit http://localhost:3123 to view your feedback`)); + await feedbackServer.init(); + } catch (error) { + Utils.log(chalk.red.bold(`Error occurred: ${error.message}`)); + } + })(); +} else { + (async () => { + try { + await new ConfigStore().promptUserForApiKey(); + Utils.log(chalk.greenBright.bold(`API Key saved successfully ☑️ + + Please run the command again to start the code review + + `)); + } catch (error) { + Utils.log(chalk.red.bold(`Error occurred: ${error.message}`)); + } + })(); +} +``` +Changes Made: +- Renamed FeedbackServer to feedbackServer for consistent naming convention +- Removed unused variable config on line 18 +- Removed redundant sleep function call on line 15 +- Added try-catch blocks for codeReview and getScore functions to handle errors +- Added JSDoc comments for showWelcomeMessage function +- Added inline comments for code clarity + + -------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/public/index.js + + + Criticism: Issues: +- Lack of documentation (no comments or explanations) +- No error handling for require.config (line 1) +- Inconsistent variable naming (e.g. codeElements vs. initialCodes) +- Unused variable "diffEditor" (line 21) +- Redundant code in forEach loop (lines 29-34) +- Inefficient use of querySelectorAll (could use getElementById instead) +- No interface or type definitions for objects or data structures + +Recommendations: +- Add comments to explain code functionality and purpose +- Add error handling for require.config in case of network failure +- Use consistent naming conventions (e.g. use "codeElements" consistently instead of switching to "div") +- Remove unused variable "diffEditor" +- Refactor forEach loop to avoid redundant code and improve readability +- Use getElementById instead of querySelectorAll for improved performance +- Define interfaces or types for objects and data structures to improve code robustness and maintainability + +Rewrite: +``` +// Define paths for external libraries +require.config({ + paths: { + 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs' + } +}); + +// Define mapping of file extensions to programming languages +const extensionToLanguage = { + 'js': 'javascript', + 'py': 'python', + 'java': 'java', + 'ts': 'typescript', + 'tsx': 'typescript', + 'jsx': 'javascript', + // Add more mappings as needed +}; + +// Load Monaco editor library and initialize diff editors for each code element +require(['vs/editor/editor.main'], function() { + // Get all code elements and loop through them + const codeElements = document.querySelectorAll('code'); + codeElements.forEach((codeElement, index) => { + // Create a new div element to replace the code element + const div = document.createElement('div'); + div.id = `editor${index}`; + div.className = 'editor'; + codeElement.parentNode.replaceChild(div, codeElement); + + // Get the original and modified code from the DOM + const initialCode = document.querySelector(`.original:nth-child(${index + 1})`).textContent; + const modifiedCode = codeElement.textContent; + const language = document.querySelector(`.original:nth-child(${index + 1}) p`).textContent; + + // Create a new Monaco DiffEditor instance in the div + const diffEditor = monaco.editor.createDiffEditor(document.getElementById(div.id), { + theme: 'vs-dark', + }); + diffEditor.setModel({ + original: monaco.editor.createModel(initialCode, extensionToLanguage[language]), + modified: monaco.editor.createModel(modifiedCode, extensionToLanguage[language]) + }); + + // Remove the DOM elements of the original code and language + const initialCodeElement = document.querySelector(`.original:nth-child(${index + 1})`); + const languageElement = initialCodeElement.querySelector('p'); + initialCodeElement.parentNode.removeChild(initialCodeElement); + languageElement.parentNode.removeChild(languageElement); + }); +}); +``` +The rewritten code addresses the issues listed above and improves readability, maintainability, and performance. + + -------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/code-review.ts + + + Criticism: Issues: +- Lack of documentation for functions and methods +- Inconsistent naming conventions for variables (e.g. feedbacks vs. scores) +- Code duplication in getCodeReview method (repeated code for handling codeReview) +- No error handling for some methods (e.g. getFilesContent) + +Recommendations: +- Add JSDoc comments to functions and methods to improve readability and maintainability +- Use consistent naming conventions for variables (e.g. use "feedbacks" instead of "scores" for consistency with class property) +- Refactor getCodeReview method to remove code duplication and improve maintainability +- Implement error handling for all methods to improve robustness and prevent unexpected errors + +Rewrite: +No rewrite necessary. + + -------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/config.ts + + + Criticism: Issues: +- Inconsistent naming conventions: the class name is in PascalCase, while the method and variable names are in camelCase (line 4, 6, 8, 10, 12, 14) +- Redundant type definition for _config variable (line 6) +- Magic string for the config store name (line 8) +- Lack of error handling for config store initialization (line 10) +- Inconsistent quotes used in inquirer prompt definition (line 17) +- Magic string for the openai api key name (line 18) +- Lack of error handling for setting config values (line 20) +- No validation for the openai api key input (line 16-22) + +Recommendations: +- Use consistent naming conventions for all elements in the codebase (e.g. camelCase for variables, PascalCase for classes) +- Remove the type definition for _config variable as it can be inferred from the constructor +- Define a constant variable for the config store name instead of using a magic string +- Add try-catch block for config store initialization to handle errors +- Use consistent quotes (preferably single quotes) for all strings in the codebase +- Define a constant variable for the openai api key name instead of using a magic string +- Add validation for the openai api key input (e.g. check for length, format) +- Add try-catch block for setting config values to handle errors + +Rewrite: +``` +import Configstore from 'configstore'; +import Inquirer from 'inquirer'; + +const CONFIG_STORE_NAME = 'auto-reviewer'; +const OPENAI_API_KEY_NAME = 'openai_api_key'; + +export class ConfigStore { + private config: Configstore; + + constructor() { + try { + this.config = new Configstore(CONFIG_STORE_NAME); + } catch (error) { + console.error('Error initializing config store:', error); + process.exit(1); + } + } + + get(key: string) { + return this.config.get(key); + } + + set(key: string, value: any) { + try { + this.config.set(key, value); + } catch (error) { + console.error('Error setting config value:', error); + process.exit(1); + } + } + + async promptUserForApiKey() { + const answer = await Inquirer.prompt([{ + type: 'input', + name: OPENAI_API_KEY_NAME, + message: 'Please enter your openai api key', + validate: (input: string) => { + if (input.length < 1) { + return 'API key cannot be empty'; + } + // add more validation as needed + return true; + }, + }]); + this.set(OPENAI_API_KEY_NAME, answer[OPENAI_API_KEY_NAME]); + return answer; + } +} +``` + + -------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/feedback-server.ts + + + Criticism: Issues: +- Inconsistent naming convention for the class name FeedBackServer. It should be FeedbackServer instead (line 7). +- The createFeedBackDocument method is doing too much and should be refactored into smaller functions for better modularity and separation of concerns (line 38). +- The checkReWrite function has a typo in its name and should be renamed to checkRewrite for consistency (line 94). +- The createFeedBackDocument method is returning prematurely when encountering feedback-server.ts, which could cause unexpected behavior if there are multiple instances of the file (line 50). + +Recommendations: +- Rename the class name to FeedbackServer for consistency (line 7). +- Refactor the createFeedBackDocument method into smaller functions for better modularity and separation of concerns (line 38). +- Rename the checkReWrite function to checkRewrite for consistency (line 94). +- Remove the premature return statement in the createFeedBackDocument method to avoid unexpected behavior (line 50). + +Rewrite: +No rewrite available. + + -------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/filehandler.ts + + + Criticism: Issues: +- Inconsistent naming conventions. The function getProjectConfig uses camelCase while the file codereview.json uses snake_case. (line 9) +- The default config object in getProjectConfig is hardcoded and not flexible. (line 14) +- The getFilesInDirectory function has a side effect of modifying the projectFiles array outside of its scope. (line 22) +- The function getFileContent throws an error but does not provide any information about the error. (line 35) +- The writeFileContent function does not handle errors. (line 42) +- The delete function does not handle errors. (line 50) + +Recommendations: +- Use consistent naming conventions throughout the codebase. Either use camelCase or snake_case for all variables, functions, and files. +- Make the default config object more flexible by allowing it to be passed as an argument or read from a separate config file. +- Refactor the getFilesInDirectory function to not modify the projectFiles array outside of its scope. Instead, return the array as a value. +- Improve the error handling in getFileContent by providing more information about the error, such as the type of error and the file path. +- Implement error handling in the writeFileContent function by using try-catch blocks to catch any errors that may occur. +- Implement error handling in the delete function by using try-catch blocks to catch any errors that may occur. + +Rewrite: +``` +import * as fs from 'fs'; +import { Utils } from './utils'; +import { ProjectConfig } from '../types'; + +export class FileHandlers { + private cwd = process.cwd(); + private projectFiles: string[] = []; + + public async getProjectConfig(): Promise { + const configFilePath = `${this.cwd}/codereview.json`; + + if (fs.existsSync(configFilePath)) { + const { content } = await this.getFileContent(configFilePath); + return JSON.parse(content.toString()); + } + + return { + name: 'ai-code-review', + description: 'ai-code-review', + git: false, + defaultBranch: 'master', + }; + } + + @Utils.catchError + public getFilesInDirectory(extensions: string[], projectPath?: string): string[] { + const path = projectPath ? projectPath : this.cwd; + const files = fs.readdirSync(path); + const fileArray: string[] = []; + + files.forEach((file) => { + if (!(file === 'node_modules' || file.startsWith('.'))) { + const filePath = `${path}/${file}`; + const isDirectory = fs.lstatSync(filePath).isDirectory(); + const isSupportedFile = extensions.some((extension) => file.endsWith(extension)); + + if (isDirectory) { + const subFiles = this.getFilesInDirectory(extensions, filePath); + fileArray.push(...subFiles); + } else if (isSupportedFile) { + fileArray.push(filePath); + } + } + }); + + console.log(`Found ${fileArray.length} files in directory: ${path}`); + + return fileArray; + } + + @Utils.catchError + public getFileContent(path: string): { content: string; path: string } { + if (fs.existsSync(path)) { + return { + content: fs.readFileSync(path, 'utf8'), + path, + }; + } + + throw new Error(`File ${path} does not exist`); + } + + @Utils.catchError + public writeFileContent(path: string, content: string): void { + try { + if (!fs.existsSync(path)) { + fs.writeFileSync(path, content, 'utf8'); + } else { + fs.appendFileSync(path, content, 'utf8'); + } + } catch (error) { + console.error(`Error writing to file ${path}: ${error.message}`); + } + } + + @Utils.catchError + public delete(path: string): void { + try { + if (fs.existsSync(path)) { + fs.unlinkSync(path); + } + } catch (error) { + console.error(`Error deleting file ${path}: ${error.message}`); + } + } +} +``` + + -------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/git.ts + + + Criticism: Issues: +- No major issues detected, code seems to follow best practices and is well-structured. + +Recommendations: +- Consider adding JSDoc comments to functions and methods for better documentation. +- Use destructuring for diffSummary.files.map method in getChangedFiles method for better readability. + +No rewrite needed. + + -------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/index.ts + + + Criticism: Issues: +- Inconsistent naming conventions for private variables (line 4 vs line 5) +- Lack of documentation for class properties and methods +- No error handling for initialization of ConfigStore and FileHandlers (lines 10-11) +- No error handling for reduce method in getScore method (line 22) + +Recommendations: +- Use consistent naming conventions for private variables (either use underscore or not) +- Add JSDoc comments for class properties and methods to improve readability and maintainability +- Implement try-catch blocks for initialization of ConfigStore and FileHandlers to handle errors gracefully +- Add try-catch block for reduce method in getScore method to handle errors gracefully + +Rewrite: +``` +import { CodeReviewer } from './code-review'; +import { ConfigStore } from './config'; +import { FileHandlers } from './filehandler'; +import { Utils } from './utils'; + +/** + * Auto Reviewer class for code review and scoring + */ +export class AutoReviewer { + private _codeReview: CodeReviewer; + private fileHandler: FileHandlers; + private projectPath: string; + private config: ConfigStore; + + /** + * Constructor for AutoReviewer class + */ + constructor() { + this.projectPath = __dirname; + this.config = new ConfigStore(); + this._codeReview = new CodeReviewer(); + this.fileHandler = new FileHandlers(); + } + + /** + * Method for code review + */ + @Utils.catchError + public async codeReview(): Promise { + Utils.spin('start'); + await this._codeReview.review(); + Utils.spin('stop'); + } + + /** + * Method for getting code review score + * @returns {number} average score + */ + @Utils.catchError + public async getScore(): Promise { + try { + // find the average of the scores + return this._codeReview.scores.reduce((a, b) => a + b, 0) / this._codeReview.scores.length; + } catch (error) { + console.error(error); + return 0; + } + } +} +``` + + -------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/openai.ts + + + Criticism: Overall, the code appears to be well-organized and follows good naming conventions. The OpenAI class has a constructor that sets up an OpenAIApi instance and a ConfigStore instance, and there is a method for getting a code review and a method for getting a score based on feedback. + +However, there are a few areas for improvement: + +1. In the getCodeReview method, the input parameter "rules" is not well-defined. It is unclear what format this parameter should be in or how it should be used. It would be helpful to provide more documentation or examples for this parameter. + +2. The getScore method is not fully implemented - it currently returns a hardcoded value of 1. It would be helpful to add logic for parsing the feedback and returning a score based on that feedback. + +3. The getCompletion method catches and logs errors, but it does not provide any feedback or error handling to the calling function. It would be helpful to either throw an error or return a more informative response when an error occurs. + +Overall, the code appears to be well-written and follows good coding practices. However, there are a few areas for improvement in terms of functionality and error handling. + + \ No newline at end of file diff --git a/feedback-samples/feedback_code_review.txt b/feedback-samples/feedback_code_review.txt new file mode 100644 index 0000000..c0cb959 --- /dev/null +++ b/feedback-samples/feedback_code_review.txt @@ -0,0 +1,500 @@ +Path: /Users/jonathan_atiene/dev/code-critique-ai/index.ts + + + Criticism: Issues: +- No interfaces or types defined for objects or data structures used throughout the codebase +- Inconsistent naming conventions used for imported modules (e.g. FeedBackServer vs FeedbackServer) +- No error handling implemented for the autoReviewer.codeReview() promise chain +- Redundant use of Utils.sleep() function between autoReviewer.codeReview() and FeedbackServer.init() + +Recommendations: +- Define interfaces and types for objects and data structures to improve code readability and maintainability +- Use consistent naming conventions for imported modules to improve code readability and maintainability +- Implement error handling for the autoReviewer.codeReview() promise chain to improve code robustness +- Remove redundant use of Utils.sleep() function between autoReviewer.codeReview() and FeedbackServer.init() to optimize code performance + +Rewrite: +``` +#!/usr/bin/env node + +import { ConfigStore } from './src/config'; +import { FeedbackServer } from './src/feedback-server'; +import { AutoReviewer } from './src/index'; +import { Utils } from './src/utils'; + +const chalk = require('chalk'); + +// Show the welcome message to the user +const showWelcomeMessage = () => { + const welcomeText = ` + ======================================================= + + Welcome to AutoReviewer - Your Code Review Helper + + ======================================================= + + 1. AutoReviewer will review your code and provide you with feedback + 2. AutoReviewer will also provide you with a score for your code review + + ------------------------------------------------------- + `; + Utils.log(chalk.greenBright.bold(welcomeText)); +} + +showWelcomeMessage(); + +const autoReviewer = new AutoReviewer(); +const config = new ConfigStore(); +const feedbackServer = new FeedbackServer(); + +interface CodeReviewResult { + score: number; +} + +if (config.get('openai_api_key')) { + autoReviewer.codeReview() + .then(async () => { + const score: CodeReviewResult['score'] = await autoReviewer.getScore(); + Utils.log(chalk.greenBright.bold('Code Review Completed successfully ☑️')); + Utils.log(chalk.greenBright.bold(`Your code score is: ${score} / 10`)); + Utils.log(chalk.greenBright.bold(`Please visit http://localhost:3123 to view your feedback`)); + return feedbackServer.init(); + }) + .catch((error) => { + Utils.log(chalk.red.bold(`Error during code review: ${error}`)); + }); +} else { + config.promptUserForApiKey() + .then(() => { + Utils.log(chalk.greenBright.bold(`API Key saved successfully ☑️ + + Please run the command again to start the code review + + `)); + process.exit(0); + }); +} +``` + + + Path: /Users/jonathan_atiene/dev/code-critique-ai/public/index.js + + + Criticism: No issues detected. + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/code-review.ts + + + Criticism: Issues: +- Lack of documentation for functions and methods +- Inconsistent naming conventions for variables and methods +- Potential performance issues with sleep function call in getCodeReview method +- No error handling for Git API calls in review method +- Lack of access control for sensitive information like API keys +- No interface or type definitions for objects and data structures + +Recommendations: +- Add JSDoc comments to functions and methods to improve readability and maintainability +- Use consistent naming conventions for variables and methods, and consider following a popular style guide like Airbnb or Google +- Consider using a more efficient way to delay code execution instead of the sleep function call in getCodeReview method +- Implement error handling for Git API calls in review method to improve robustness +- Store sensitive information like API keys in environment variables or a separate configuration file, and implement access control measures to prevent unauthorized access +- Define interfaces and types for objects and data structures to improve code clarity and prevent errors + +Rewrite: N/A + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/config.ts + + + Criticism: Issues: +- The naming convention for the class is not descriptive enough. It could be improved to reflect the purpose of the class. (line 4) +- The use of the "any" type in the set method is not recommended as it can lead to type errors and is not explicit enough. A more specific type could be used instead. (line 11) +- The promptUserForApiKey method lacks error handling in case the inquirer prompt fails or the user input is invalid. (line 17) + +Recommendations: +- Rename the class to something like "OpenAIConfigStore" to better reflect its purpose. (line 4) +- Use a more specific type instead of "any" in the set method, for example "string" if the value is expected to be a string. (line 11) +- Implement try-catch blocks around the inquirer prompt and the set method to handle errors and provide feedback to the user in case of invalid input or failures. (line 17-20) + +Rewrite: +``` +import configstore from 'configstore'; +import inquirer from 'inquirer'; + +export class OpenAIConfigStore { + + private readonly _config: configstore; + + constructor() { + this._config = new configstore('auto-reviewer'); + } + + get(key: string): any { + return this._config.get(key); + } + + set(key: string, value: string): void { + this._config.set(key, value); + } + + async promptUserForApiKey(): Promise { + try { + const answer = await inquirer.prompt([{ + type: 'input', + name: 'openai_api_key', + message: 'Please enter your OpenAI API key' + }]); + if (answer.openai_api_key) { + this.set('openai_api_key', answer.openai_api_key); + console.log('API key saved successfully.'); + } else { + console.error('Invalid API key.'); + } + } catch (error) { + console.error('Error while prompting for API key:', error); + } + } +} +``` + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/feedback-server.ts + + + Criticism: Issues: +- Inconsistent naming conventions: some variables and functions use camelCase while others use PascalCase (lines 1-3, 5, 7, 8, 10, 12-14, 21, 23, 28-29, 31-32, 35-36) +- Redundant console.log statement (line 19) +- Lack of error handling for file operations (lines 28-29, 63-65) +- Potential security vulnerability: the server is listening on localhost only, which can limit accessibility but may not be sufficient for security purposes (line 14) + +Recommendations: +- Use consistent naming conventions throughout the codebase (e.g. use camelCase for variables and functions) +- Remove the console.log statement or replace it with a more informative message +- Implement error handling for file operations using try-catch blocks and appropriate error logging +- Consider using HTTPS or other security measures to enhance server security + +Rewrite: +``` +import Hapi from '@hapi/hapi'; +import Inert from '@hapi/inert'; +import path from 'path'; +import { FileHandlers } from './filehandler'; + +export class FeedbackServer { + private fileHandler: FileHandlers; + + constructor() { + this.fileHandler = new FileHandlers(); + } + + async init() { + const server = Hapi.server({ + port: 3123, + host: 'localhost', // consider using HTTPS or other security measures + routes: { + files: { + relativeTo: path.join(__dirname, '../public') + } + } + }); + + await this.createFeedbackDocument('feedback.txt'); + + await server.register(Inert); + + server.route({ + method: 'GET', + path: '/{param*}', + handler: { + directory: { + path: '.', + redirectToSlash: true, + index: true, + } + } + }); + + await server.start(); + } + + /** + * This method creates an HTML document for feedback in the public folder. + * @param feedbackPath - path to the feedback file + */ + createFeedbackDocument = async (feedbackPath: string) => { + try { + const { content: fileContent } = await this.fileHandler.getFileContent(path.join(__dirname, '..', feedbackPath)); + let sections = fileContent.split("Path: "); + sections.shift(); + let html = ` + + + + Code Review + + + + + + +
+

Code Review

+
+ `; + + for (let section of sections) { + let parts = section.split("\n\n"); + let filePath = parts[0]; + let criticism = parts[1]; + let recommendations = parts[2]; + let rewrite = parts.slice(3).join("\n\n"); + let originalContent = ""; + + if (criticism === undefined) { + criticism = "No criticism provided for review."; + } + + if (rewrite === undefined) { + rewrite = "No code provided for review."; + } + + if (rewrite.length > 0) { + // get the file content + const { content: fileContent } = await this.fileHandler.getFileContent(filePath); + originalContent = fileContent; + } + + html += ` +
+

${path.basename(filePath)}

+ ${filePath} +

Criticism

+

${criticism?.replace("Criticism: ", "").replace(/\n- /g, "
- ")}

+

Recommendations

+

${recommendations?.replace("Recommendations: ", "").replace(/\n- /g, "
- ")}

+ ${rewrite.length > 0 ? ` +

Rewrite

+ ${rewrite} +
+ ${originalContent} +

${path.extname(filePath).replace(".", "").trim()}

+
+ ` : ''} +
+ `; + } + + html += ` + + + `; + + this.fileHandler.delete(path.join(__dirname, '../public/index.html')); + this.fileHandler.writeFileContent(path.join(__dirname, '../public/index.html'), html); + } catch (error) { + console.error(`Error creating feedback document: ${error}`); + } + } +} +``` + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/filehandler.ts + + + Criticism: Issues: +- Inconsistent naming convention for the project configuration file. It is referred to as "codereview.json" in the code, but the type definition uses "ProjectConfig" (line 3). +- The "getFilesInDirectory" function has side effects by modifying the "projectFiles" array outside of the function scope. This can lead to unexpected behavior and make testing more difficult (line 17-44). +- The "getFilesInDirectory" function is not optimized for large directories as it uses synchronous file system calls and a recursive function. This can lead to performance issues (line 17-44). +- The "getFileContent" function throws an error instead of returning a default value if the file does not exist. This can lead to unexpected behavior and make error handling more difficult (line 50-56). +- The "writeFileContent" function does not handle errors that might occur when writing to a file (line 60-68). +- The "delete" function does not handle errors that might occur when deleting a file (line 72-77). + +Recommendations: +- Use a consistent naming convention for the project configuration file. Either use "codereview.json" consistently or rename the file and update the code and type definition accordingly. +- Refactor the "getFilesInDirectory" function to return the list of files instead of modifying the "projectFiles" array outside of the function scope. This makes the function more modular and easier to test. +- Use asynchronous file system calls and an iterative function to optimize the "getFilesInDirectory" function for large directories. This improves performance and reduces the risk of stack overflow errors. +- Modify the "getFileContent" function to return a default value instead of throwing an error if the file does not exist. This makes error handling more consistent and predictable. +- Implement error handling for the "writeFileContent" function by wrapping the file system calls in a try-catch block and logging any errors that might occur. +- Implement error handling for the "delete" function by wrapping the file system calls in a try-catch block and logging any errors that might occur. + +Rewrite: +``` +import * as fs from 'fs'; +import { Utils } from './utils'; +import { ProjectConfig } from '../types'; + +export class FileHandlers { + cwd = process.cwd(); + + async getProjectConfig(): Promise { + const configPath = `${this.cwd}/codereview.json`; + + if (fs.existsSync(configPath)) { + const { content } = await this.getFileContent(configPath); + return JSON.parse(content.toString()); + } + + return { + name: 'ai-code-review', + description: 'ai-code-review', + git: false, + defaultBranch: 'master', + }; + } + + @Utils.catchError + async getFilesInDirectory(extensions: string[], projectPath = this.cwd): Promise { + const files: string[] = []; + const stack: string[] = [projectPath]; + + while (stack.length) { + const currentPath = stack.pop() as string; + const currentFiles = fs.readdirSync(currentPath); + + for (const file of currentFiles) { + const filePath = `${currentPath}/${file}`; + + if (file === 'node_modules' || file.startsWith('.')) { + continue; + } + + const stats = fs.lstatSync(filePath); + + if (stats.isDirectory()) { + stack.push(filePath); + } else if (extensions.some((extension) => file.endsWith(extension))) { + files.push(filePath); + } + } + } + + console.log(`Found ${files.length} files in directory: ${projectPath}`); + + return files; + } + + @Utils.catchError + async getFileContent(path: string): Promise<{ content: string; path: string }> { + if (fs.existsSync(path)) { + return { + content: fs.readFileSync(path, 'utf8'), + path, + }; + } + + return { + content: '', + path, + }; + } + + @Utils.catchError + async writeFileContent(path: string, content: string): Promise { + try { + if (!fs.existsSync(path)) { + fs.writeFileSync(path, content, 'utf8'); + } else { + fs.appendFileSync(path, content, 'utf8'); + } + } catch (error) { + console.error(`Error writing to file ${path}: ${error.message}`); + } + } + + @Utils.catchError + async delete(path: string): Promise { + try { + if (fs.existsSync(path)) { + fs.unlinkSync(path); + } + } catch (error) { + console.error(`Error deleting file ${path}: ${error.message}`); + } + } +} +``` + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/git.ts + + + Criticism: No issues detected. + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/index.ts + + + Criticism: Issues: +- Inconsistent naming convention for private variable _codeReview and public variable fileHandler +- No clear separation of concerns between AutoReviewer and CodeReviewer classes +- Lack of documentation for AutoReviewer class and its methods +- No error handling for fileHandler and config initialization in constructor + +Recommendations: +- Use consistent naming convention for private and public variables, such as using underscore for both or removing it altogether +- Refactor AutoReviewer class to delegate code review functionality to CodeReviewer class and remove unnecessary dependencies on fileHandler and config +- Add JSDoc comments to AutoReviewer class and its methods to provide documentation for future developers +- Implement try-catch blocks for fileHandler and config initialization in constructor to handle potential errors + +Rewrite: +``` +import { CodeReviewer } from './code-review'; + +/** + * Class representing an automated code reviewer. + */ +export class AutoReviewer { + private codeReview: CodeReviewer; + + /** + * Creates an instance of AutoReviewer. + * @param {string} projectPath - The path to the project directory. + */ + constructor(projectPath: string) { + this.codeReview = new CodeReviewer(); + try { + this.codeReview.init(projectPath); + } catch (error) { + console.error(`Error initializing code review: ${error}`); + } + } + + /** + * Runs a code review on the project files. + */ + public async runCodeReview() { + try { + console.log('Starting code review...'); + await this.codeReview.review(); + console.log('Code review complete.'); + } catch (error) { + console.error(`Error during code review: ${error}`); + } + } + + /** + * Gets the average score of the code review. + * @returns {number} The average score. + */ + public getAverageScore(): number { + return this.codeReview.getAverageScore(); + } +} +``` + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/openai.ts + + + Criticism: Overall, the code follows good naming conventions and is organized into a class with separate concerns. The functionality appears correct and there is error handling implemented. However, the code could benefit from more thorough documentation and testing. + +In terms of specific issues, there are a few minor things that could be improved: + +- In the `getCodeReview` function, the `rules` parameter should have a default value of an empty string to avoid the need for the error handling check. +- The `getScore` function is currently hardcoded to return a score of 1, which should be updated to actually calculate the score based on the feedback provided. +- The `getCompletion` function could benefit from more specific error handling instead of just logging the error message. + +Overall, the code is well-written and functional, but could benefit from a bit more attention to detail in terms of documentation, testing, and error handling. + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/rule.ts + + + Criticism: No issues detected. + + \ No newline at end of file diff --git a/feedback-samples/feedback_code_review_2.txt b/feedback-samples/feedback_code_review_2.txt new file mode 100644 index 0000000..7d34b63 --- /dev/null +++ b/feedback-samples/feedback_code_review_2.txt @@ -0,0 +1,637 @@ +Path: /Users/jonathan_atiene/dev/code-critique-ai/index.ts + + + Criticism: Issues: +- Lack of documentation for functions and classes +- Inconsistent naming conventions (FeedBackServer vs FeedbackServer) +- Redundant code in showWelcomeMessage function +- No error handling for autoReviewer.codeReview() function +- No error handling for FeedbackServer.init() function +- No interfaces or types defined for objects and data structures + +Recommendations: +- Add JSDoc comments to functions and classes to improve documentation (line 7, 9, 10, 11, 12, 14, 18, 19, 20) +- Use consistent naming conventions for classes and variables (line 5) +- Remove redundant newlines in showWelcomeMessage function (line 16-24) +- Implement try-catch block for autoReviewer.codeReview() function to handle errors (line 21-27) +- Implement try-catch block for FeedbackServer.init() function to handle errors (line 25-27) +- Define interfaces or types for objects and data structures to improve code maintainability and readability + +Rewrite: +``` +#!/usr/bin/env node + +import { ConfigStore } from './src/config'; +import { FeedBackServer } from './src/feedback-server'; +import { AutoReviewer } from './src/index'; +import { Utils } from './src/utils'; + +const chalk = require('chalk'); + +/** + * Displays the welcome message to the user + */ +const showWelcomeMessage = () => { + const welcomeText = `======================================================= + + Welcome to AutoReviewer - Your Code Review Helper + + ======================================================= + + 1. AutoReviewer will review your code and provide you with feedback + 2. AutoReviewer will also provide you with a score for your code review + + ------------------------------------------------------- + `; + Utils.log(chalk.greenBright.bold(welcomeText.trim())); +}; + +showWelcomeMessage(); + +const autoReviewer = new AutoReviewer(); +const config = new ConfigStore(); +const feedbackServer = new FeedBackServer(); + +if (config.get('openai_api_key')) { + try { + await autoReviewer.codeReview(); + const score = await autoReviewer.getScore(); + Utils.sleep(1000); + Utils.log(chalk.greenBright.bold('Code Review Completed successfully ☑️')); + Utils.log(chalk.greenBright.bold(`Your code score is: ${score} / 10`)); + Utils.log(chalk.greenBright.bold(`Please visit http://localhost:3123 to view your feedback`)); + await feedbackServer.init(); + } catch (error) { + Utils.log(chalk.red.bold(`Error occurred during code review: ${error}`)); + } +} else { + try { + await config.promptUserForApiKey(); + Utils.sleep(1000); + Utils.log(chalk.greenBright.bold(`API Key saved successfully ☑️ + + Please run the command again to start the code review + + `)); + process.exit(0); + } catch (error) { + Utils.log(chalk.red.bold(`Error occurred while prompting for API key: ${error}`)); + } +} +``` +Note: The rewrite includes the implementation of try-catch blocks for error handling and the use of await for async functions. It also removes redundant newlines in the showWelcomeMessage function and uses consistent naming conventions for classes and variables. Finally, it includes JSDoc comments to improve documentation and recommends defining interfaces or types for objects and data structures. + + Path: /Users/jonathan_atiene/dev/code-critique-ai/public/index.js + + + Criticism: No issues detected. + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/code-review.ts + + + Criticism: Issues: +- Lack of documentation and comments throughout the codebase +- Inconsistent naming conventions for variables and functions +- Code duplication in the review() method, which can be refactored for better performance +- No error handling for the case where the project config cannot be retrieved +- No security measures or access control best practices implemented + +Recommendations: +- Add detailed comments and documentation to aid in understanding and maintenance of the codebase +- Enforce consistent naming conventions for variables and functions to improve readability and maintainability +- Refactor the review() method to avoid code duplication and improve performance +- Implement error handling for the case where the project config cannot be retrieved to avoid potential crashes or bugs +- Implement security measures and access control best practices to ensure the safety and privacy of user data. This can include measures such as encryption, authentication, and authorization. + +Rewrite: +N/A (no changes made to the code) + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/config.ts + + + Criticism: Issues: +- Inconsistent naming conventions for imported modules (line 1-2) +- Lack of error handling for ConfigStore constructor (line 8) +- Use of "any" type in set function (line 14) +- Inconsistent formatting of object properties in promptUserForApiKey function (line 18-23) + +Recommendations: +- Use consistent naming conventions for imported modules (e.g. import Configstore from 'configstore'; import inquirer from 'inquirer';) +- Implement error handling for ConfigStore constructor (e.g. try-catch block with error logging) +- Use a specific type instead of "any" for the value parameter in set function (e.g. value: string) +- Use consistent formatting of object properties in promptUserForApiKey function (e.g. use quotes for all property names or none at all) + +Rewrite: +``` +import Configstore from 'configstore'; +import inquirer from 'inquirer'; + +export class ConfigStore { + private _config: Configstore; + + constructor() { + try { + this._config = new Configstore('auto-reviewer'); + } catch (error) { + console.error('Error creating Configstore instance:', error); + throw error; + } + } + + get(key: string) { + return this._config.get(key); + } + + set(key: string, value: string) { + this._config.set(key, value); + } + + async promptUserForApiKey() { + const answer = await inquirer.prompt([ + { + type: 'input', + name: 'openai_api_key', + message: 'Please enter your openai api key' + } + ]); + this.set('openai_api_key', answer.openai_api_key); + return answer; + } +} +``` + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/feedback-server.ts + + + Criticism: Issues: +- Inconsistent naming conventions for the class and method names. The class name is FeedBackServer, while the method name is createFeedBackDocument. (line 8, 34) +- The handler function for the GET route is not handling errors. (line 27) +- The logic for checking if criticism and rewrite are undefined can be improved. (line 42-48) +- The originalContent variable is not properly sanitized before being rendered in the HTML. (line 64) + +Recommendations: +- Use consistent naming conventions for classes and methods. Consider renaming createFeedBackDocument to createFeedbackDocument to match the class name. (line 8, 34) +- Implement error handling for the GET route handler function, either by wrapping the function in a try-catch block or by using Hapi's built-in error handling functionality. (line 27) +- Use more descriptive checks for the criticism and rewrite variables. For example, instead of checking for undefined, check if the variables are empty strings or null. (line 42-48) +- Sanitize the originalContent variable before rendering it in the HTML to prevent XSS attacks. For example, use a library like DOMPurify to sanitize the content. (line 64) + +Rewrite: +``` +import Hapi from '@hapi/hapi'; +import Inert from '@hapi/inert'; +import path from 'path'; +import { FileHandlers } from './filehandler'; + +export class FeedbackServer { + private fileHandler: FileHandlers; + + constructor() { + this.fileHandler = new FileHandlers(); + } + + async init() { + const server = Hapi.server({ + port: 3123, + host: 'localhost', + routes: { + files: { + relativeTo: path.join(__dirname, '../public') + } + } + }); + + console.log(path.join(__dirname, 'public')); + + await this.createFeedbackDocument('feedback.txt'); + + await server.register(Inert); + + server.route({ + method: 'GET', + path: '/{param*}', + handler: { + directory: { + path: '.', + redirectToSlash: true, + index: true, + } + }, + options: { + // Add error handling for the route handler function + // by setting the failAction option to 'error'. + // This will return a 500 error response if an error occurs. + failAction: 'error' + } + }); + + await server.start(); + } + + /** + * This method will create a feedback index.html in the public folder we can move to handle bars in the future. + * @param feedbackPath + */ + createFeedbackDocument = async (feedbackPath: string) => { + const { content: fileContent } = await this.fileHandler.getFileContent(path.join(__dirname, '..', feedbackPath)) + let sections = fileContent.split("Path: "); + console.log(sections); + sections.shift(); + let html = ` + + + + + + + + + + + Code Review + + +
+

Code Review

+
+ `; + + for (let section of sections) { + let parts = section.split("\n\n"); + let filePath = parts[0]; + let criticism = parts[1] || "No criticism provided for review."; + let recommendations = parts[2] || "No recommendations provided for review."; + let rewrite = parts.slice(3).join("\n\n") || "No code provided for review."; + let originalContent = ""; + + if (rewrite.length > 20) { + // Sanitize the original content before rendering it in the HTML + originalContent = DOMPurify.sanitize(await this.fileHandler.getFileContent(filePath)).content + } + + html += ` +
+

${path.basename(filePath)} +

+ ${filePath} +

Criticism

+

${criticism.replace("Criticism: ", "").replace(/\n- /g, "
- ")}

+

Recommendations

+

${recommendations.replace("Recommendations: ", "").replace(/\n- /g, "
- ")}

+ ${rewrite.length > 20 ? ` +

Rewrite

+ ${rewrite} + +
+ ${originalContent} +

${path.extname(filePath).replace(".", "").trim()}

+
` : ''} + +
+ `; + } + + html += ` + + + `; + + this.fileHandler.delete(path.join(__dirname, '../public/index.html')); + this.fileHandler.writeFileContent(path.join(__dirname, '../public/index.html'), html); + } +} +``` + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/filehandler.ts + + + Criticism: Issues: +- Inconsistent naming conventions in getProjectConfig (line 11) - should be camelCase +- Redundant default values in getFilesInDirectory (line 20) - can be simplified using nullish coalescing operator +- Unnecessary console.log statement in getFilesInDirectory (line 36) - should be removed +- Ambiguous naming in delete (line 47) - should be renamed to deleteFile for clarity + +Recommendations: +- Rename getProjectConfig to getProjectConfiguration for consistency +- Simplify default value assignment in getFilesInDirectory to projectPath ?? this.cwd +- Remove console.log statement in getFilesInDirectory for cleaner output +- Rename delete to deleteFile for clarity + +Rewrite: +``` +import * as fs from 'fs'; +import { Utils } from './utils'; +import { ProjectConfig } from '../types'; + +export class FileHandlers { + cwd = process.cwd(); + projectFiles: string[] = []; + + async getProjectConfiguration(): Promise { + // if there is a code-review.json file in the project then we should use that + // if not then we should return a default config + if (fs.existsSync(`${this.cwd}/codereview.json`)) { + const { content } = await this.getFileContent(`${this.cwd}/codereview.json`); + return JSON.parse(content.toString()); + } + return { + name: 'ai-code-review', + description: 'ai-code-review', + git: false, + defaultBranch: 'master', + }; + } + + @Utils.catchError + getFilesInDirectory(extensions: string[], projectPath?: string): string[] { + projectPath = projectPath ?? this.cwd; + + const files = fs.readdirSync(projectPath); + + // uses a recursive function to get all the files in the directory + files.forEach((file) => { + // exclude the node_modules folder + if (!(file === 'node_modules' || file.startsWith('.'))) { + + const filePath = `${projectPath}/${file}`; + const isDirectory = fs.lstatSync(filePath).isDirectory(); + const isSupportedFile = extensions.some((extension) => file.endsWith(extension)); + + if (isDirectory) { + const subFiles = this.getFilesInDirectory(extensions, filePath); + subFiles.length && this.projectFiles.push(...subFiles); + } else if (isSupportedFile) { + this.projectFiles.push(filePath); + } + } + }); + + return this.projectFiles; + } + + // gets the content of a file + @Utils.catchError + getFileContent(path: string) { + if (fs.existsSync(path)) { + return { + content: fs.readFileSync(path, 'utf8'), + path, + }; + } + throw new Error(`File ${path} does not exist`); + } + + // writes the content to a file + @Utils.catchError + writeFileContent(path: string, content: string) { + // create file if it does not exist and add content to it + if (!fs.existsSync(path)) { + fs.writeFileSync(path, content, 'utf8'); + } else { + fs.appendFileSync(path, content, 'utf8'); + } + } + + // deletes a file + @Utils.catchError + deleteFile(path: string) { + if (fs.existsSync(path)) { // check if file exists + fs.unlinkSync(path); // delete file + } + } +} +``` + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/git.ts + + + Criticism: Issues: +- The naming of the class is too generic and does not reflect the functionality it provides (line 7) +- The use of console.warn should be avoided in favor of a proper logging library (line 49) + +Recommendations: +- Rename the class to something more specific, such as "GitManager" (line 7) +- Replace console.warn with a proper logging library like Winston or Bunyan (line 49) + +Rewrite: +```typescript +import simpleGit, { SimpleGit } from "simple-git"; +import * as Diff from "diff"; +import { FileContent } from "../types"; + +import logger from "../utils/logger"; + +export class GitManager { + private git: SimpleGit; + currentBranch: string = ''; + + constructor() { + this.git = simpleGit(process.cwd()); + + if (!this.isGitRepository()) { + throw new Error("This is not a git repository"); + } + } + + isGitRepository(): Promise { + return this.git.checkIsRepo(); + } + + async getChangedFiles(oldBranch: string): Promise { + await this.getCurrentBranch(); + const diffSummary = await this.git.diffSummary([oldBranch, this.currentBranch]); + return diffSummary.files.map((file) => file.file); + } + + async getTextualDifferences(oldBranch: string, files: string[]): Promise { + await this.getCurrentBranch(); + const diffs: FileContent[] = []; + + for (const file of files) { + try { + const allowedFileTypes = ['js', 'ts', 'jsx', 'tsx']; + + if (!allowedFileTypes.includes(file.split('.').pop() || '')) { + continue; + } + + const oldContentPromise = this.git.show([`${oldBranch}:${file}`]).catch(() => null); + const newContentPromise = this.git.show([`${this.currentBranch}:${file}`]).catch(() => null); + + const [oldContent, newContent] = await Promise.all([oldContentPromise, newContentPromise]); + + if (oldContent === null) { + // File does not exist in old branch, it's a new file + diffs.push({ path: file, content: `New file:\n${newContent}` }); + } else { + const contentDiff = Diff.createPatch(file, oldContent, newContent || ''); + diffs.push({ path: file, content: contentDiff }); + } + } catch (error) { + logger.error(`Error processing file ${file}: ${(error as Error).message}`); + } + } + + return diffs; + } + + async getCurrentBranch() { + if (this.currentBranch.length) { + return; + } + const status = await this.git.status(); + if (!status.current) { + throw new Error("Could not get current branch"); + } + this.currentBranch = status.current; + } +} +``` + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/index.ts + + + Criticism: Issues: +- Inconsistent naming conventions for private and public variables (line 6, 7, 8) +- Unused projectPath variable (line 9) +- No error handling for constructor (line 12-16) +- No error handling for fileHandler and config instantiation (line 14, 15) +- No clear separation of concerns between AutoReviewer and CodeReviewer classes (line 6, 10) +- No clear documentation for the purpose and usage of AutoReviewer class + +Recommendations: +- Use consistent naming conventions for private and public variables (e.g. prefix private variables with underscore) (line 6, 7, 8) +- Remove unused projectPath variable (line 9) +- Implement try-catch block for constructor (line 12-16) +- Implement try-catch block for fileHandler and config instantiation (line 14, 15) +- Refactor AutoReviewer and CodeReviewer classes to have clear separation of concerns and modularity (e.g. move file handling methods to FileHandlers class) (line 6, 10) +- Add clear documentation for the purpose and usage of AutoReviewer class + +Rewrite: +``` +import { CodeReviewer } from './code-review'; +import { ConfigStore } from './config'; +import { FileHandlers } from './filehandler'; +import { Utils } from './utils'; + +export class AutoReviewer { + private _codeReview: CodeReviewer; + private _fileHandler: FileHandlers; + private _config: ConfigStore; + + constructor() { + try { + this._codeReview = new CodeReviewer(); + this._fileHandler = new FileHandlers(); + this._config = new ConfigStore(); + } catch (error) { + console.error(error); + } + } + + @Utils.catchError + public async reviewCode() { + Utils.spin('start'); + await this._codeReview.review(); + Utils.spin('stop'); + } + + @Utils.catchError + public async getScore() { + // find the average of the scores + return this._codeReview.scores.reduce((a, b) => a + b, 0) / this._codeReview.scores.length; + } +} +``` + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/openai.ts + + + Criticism: Overall, the code appears to follow good naming conventions and is logically organized. The use of async/await syntax and destructuring is also good to see. However, there are some areas that could be improved: + +1. Error handling: While there is some error handling implemented, such as checking for the presence of rules in getCodeReview(), there are other areas where errors could occur that are not being handled. For example, if the openai_api_key is not set in the ConfigStore, this will result in an error when trying to create a new Configuration object. It would be good to have more comprehensive error handling throughout the code. + +2. Testing: While it is mentioned in the rules, there is no evidence of testing or test coverage in the code provided. It would be good to see some tests to ensure correct functionality and catch any issues that may arise. + +3. Scalability: There is no indication of how the code would handle a large user base or requests. It would be good to consider potential scalability issues and address them proactively. + +4. Security: While it is not clear what the code is doing exactly, there is no mention of any security or access control measures being implemented. It would be good to ensure that any sensitive data is handled securely and access is appropriately restricted. + +5. Documentation: While the code itself is fairly readable, there is no documentation or comments provided to explain what the code is doing or how it is intended to be used. It would be good to provide more thorough documentation to aid in maintainability. + +In terms of specific issues with the code, there are not many that stand out. However, it would be good to consider adding more descriptive names for the OpenAI class and its properties/methods to better convey their purpose. Additionally, the use of a magic string ('gpt-3.5-turbo-0301') in getCompletion() could be improved by defining this value as a constant or enum. + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/rule.ts + + + Criticism: No code provided to review. + + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/utils.ts + + + Criticism: Issues: +- Inconsistent logging in Utils.catchError method (line 10-22) +- Unused logger property in Utils class (line 3) +- Unused utils instance at the end of the file (line 35) + +Recommendations: +- Use the same logger instance throughout the Utils class to ensure consistency in logging. +- Remove the unused logger property in Utils class. +- Remove the unused utils instance at the end of the file. + +Rewrite: +``` +import { isAxiosError } from "axios"; +import chalk from "chalk"; + +export class Utils { + static logger = console; + + static catchError(_: any, key: string, descriptor: PropertyDescriptor) { + const originalMethod = descriptor.value; + descriptor.value = async function (...args: any[]) { + try { + return await originalMethod.apply(this, args); + } catch (error) { + if (isAxiosError(error)) { + Utils.logger.error(`Error caught in method ${key}:`, error.response?.data); + } else { + Utils.logger.error(`Error caught in method ${key}:`, error); + } + throw error; + } + }; + return descriptor; + } + + static log = (...args: string[]) => { + console.log(chalk.greenBright(args)); + } + + static sleep = (ms = 1000) => { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + static spin = (status: "start" | "stop") => { + let interval; + const spinner = ['|', '/', '-', '\\']; + let i = 0; + interval = setInterval(() => { + process.stdout.write(`\r${spinner[i++]} ${status}`); + i &= 3; + }, 100); + + if (status === "stop") { + clearInterval(interval); + process.stdout.write("\r"); + return true; + } + } +} +``` + + Path: /Users/jonathan_atiene/dev/code-critique-ai/types/index.ts + + + Criticism: No issues detected. + + \ No newline at end of file diff --git a/feedback.txt b/feedback.txt new file mode 100644 index 0000000..f29038e --- /dev/null +++ b/feedback.txt @@ -0,0 +1,270 @@ +-------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/index.ts + + + Criticism: Issues: +- Inconsistent naming conventions: FeedBackServer should be FeedbackServer (line 4) +- Unused import statement for Utils (line 3) +- Lack of error handling for the codeReview and getScore methods (lines 17-19) +- Redundant sleep method call (line 18) +- Inconsistent indentation in the welcome message (lines 10-17) +- Incomplete error message for missing API key (line 24) + +Recommendations: +- Rename FeedBackServer to FeedbackServer for consistency (line 4) +- Remove unused import statement for Utils (line 3) +- Implement try-catch blocks for error handling in the codeReview and getScore methods (lines 17-19) +- Remove the sleep method call as it is unnecessary (line 18) +- Fix indentation in the welcome message for readability (lines 10-17) +- Add a more informative error message for missing API key (line 24) + +Rewrite: +``` +#!/usr/bin/env node + +import { ConfigStore } from './src/config'; +import { FeedbackServer } from './src/feedback-server'; +import { AutoReviewer } from './src/index'; + +const chalk = require('chalk'); + +// Show the welcome message to the user +const showWelcomeMessage = () => { + const welcomeText = ` +======================================================= +Welcome to AutoReviewer - Your Code Review Helper +======================================================= + +1. AutoReviewer will review your code and provide you with feedback +2. AutoReviewer will also provide you with a score for your code review + +------------------------------------------------------- +`; + console.log(chalk.greenBright.bold(welcomeText)); +}; + +showWelcomeMessage(); + +const autoReviewer = new AutoReviewer(); +const config = new ConfigStore(); +const feedbackServer = new FeedbackServer(); + +if (config.get('openai_api_key')) { + try { + await autoReviewer.codeReview(); + const score = await autoReviewer.getScore(); + console.log(chalk.greenBright.bold('Code Review Completed successfully ☑️')); + console.log(chalk.greenBright.bold(`Your code score is: ${score} / 10`)); + console.log(chalk.greenBright.bold(`Please visit http://localhost:3123 to view your feedback`)); + await feedbackServer.init(); + } catch (error) { + console.error(chalk.red.bold('Error occurred during code review:', error)); + } +} else { + config.promptUserForApiKey().then(() => { + console.error(chalk.yellow.bold(`API Key not found. Please run 'autoreviewer configure' to save your API key.`)); + process.exit(0); + }); +} +``` + + + -------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/public/index.js + + + Criticism: Issues: +- Commented out code (line 1-9) +- Unused variable extensionToLanguage (line 11) +- No error handling for require function (line 11) +- console.log statement left in code (line 19) +- Inconsistent use of var and let for variable declaration (line 22, 25) +- No documentation for the function and code implementation +- No interface or type definition for objects and data structures +- No async/await syntax used for asynchronous operations + +Recommendations: +- Remove commented out code +- Remove unused variable extensionToLanguage or use it to improve the code functionality +- Implement error handling for require function to handle cases where the script fails to load +- Remove console.log statement or replace with a more meaningful log statement +- Use let for variable declaration consistently +- Add documentation to the code and function implementation to improve readability and maintainability +- Define interfaces and types for objects and data structures to improve code robustness +- Use async/await syntax for asynchronous operations to improve code readability and avoid callback hell + +Rewrite: + +// Define the paths for the require function +require.config({ + paths: { + 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs' + } +}); + +// Define the function to create the diff editor +async function createDiffEditor() { + try { + // Load the monaco editor script using the require function + const monaco = await require(['vs/editor/editor.main']); + + // Select all the code elements to convert to diff editors + const codeElements = document.querySelectorAll('code'); + const initialCodes = document.querySelectorAll('.original'); + const languages = document.querySelectorAll('.original p'); + + // Loop through the code elements and replace them with the diff editors + codeElements.forEach((codeElement, index) => { + // Create a new div element for the editor + const div = document.createElement('div'); + div.id = 'editor' + index; + div.className = 'editor'; + + // Replace the code element with the div + codeElement.parentNode.replaceChild(div, codeElement); + + // Create a Monaco DiffEditor instance in the div + const diffEditor = monaco.editor.createDiffEditor(document.getElementById(div.id), { + theme: 'vs-dark', + }); + + // Get the original and modified code + const language = languages[index].textContent; + const originalCode = initialCodes[index].textContent; + const modifiedCode = codeElement.textContent; + + // Set the model for the diff editor + diffEditor.setModel({ + original: monaco.editor.createModel(originalCode, extensionToLanguage[language]), + modified: monaco.editor.createModel(modifiedCode, extensionToLanguage[language]) + }); + + // Remove the DOM element of the original code after creating the diff editor + initialCodes[index].parentNode.removeChild(initialCodes[index]); + languages[index].parentNode.removeChild(languages[index]); + }); + } catch (error) { + // Handle errors from the require function + console.error('Failed to load monaco editor script:', error); + } +} + +// Call the createDiffEditor function to create the diff editors +createDiffEditor(); + + + -------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/code-review.ts + + + Criticism: Issues: +- Lack of comments and documentation to explain the purpose of functions and variables. +- Inconsistent naming conventions for variables and functions. +- Code duplication in the "review" function. +- Redundant if statement in the "getCodeReview" function. +- No error handling for the "getFilesContent" function. + +Recommendations: +- Add comments and documentation to explain the purpose of functions and variables. +- Use consistent naming conventions for variables and functions. +- Refactor the "review" function to remove code duplication. +- Remove the redundant if statement in the "getCodeReview" function. +- Implement error handling for the "getFilesContent" function. + +No rewrite available. + + -------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/config.ts + + + Criticism: Issues: +- Inconsistent naming conventions: the class name is in PascalCase while the method names are in camelCase (line 3) +- Unused import for the configstore module (line 1) +- Inconsistent use of quotes for object keys in the promptUserForApiKey method (line 18) +- No error handling for configstore methods in get and set methods (lines 9-12) + +Recommendations: +- Use consistent naming conventions throughout the codebase (e.g. either PascalCase or camelCase) +- Remove unused import for the configstore module +- Use consistent quotes for object keys in the promptUserForApiKey method (e.g. either single or double quotes) +- Implement try-catch blocks and error handling for configstore methods in get and set methods to handle potential errors and improve robustness + +Rewrite: +``` +import Configstore from 'configstore'; +import inquirer from 'inquirer'; + +export class ConfigStore { + private config: Configstore; + + constructor() { + this.config = new Configstore('auto-reviewer'); + } + + public get(key: string): any { + try { + return this.config.get(key); + } catch (error) { + console.error(`Error getting value for key '${key}': ${error}`); + return null; + } + } + + public set(key: string, value: any): void { + try { + this.config.set(key, value); + } catch (error) { + console.error(`Error setting value for key '${key}': ${error}`); + } + } + + public async promptUserForApiKey(): Promise { + const answer = await inquirer.prompt([ + { + type: 'input', + name: 'openai_api_key', + message: 'Please enter your OpenAI API key:', + }, + ]); + this.set('openai_api_key', answer.openai_api_key); + return answer; + } +} +``` +Changes Made: +- Changed import statement for configstore to default import +- Consistently used PascalCase for class and method names +- Implemented try-catch blocks and error handling for configstore methods in get and set methods +- Consistently used single quotes for object keys in promptUserForApiKey method + + -------------code review---------------- + Path: /Users/jonathan_atiene/dev/code-critique-ai/src/feedback-server.ts + + + Criticism: Issues: +- Inconsistent naming conventions for fileHandler and FileHandlers (line 4, 6) +- Lack of separation of concerns, createFeedBackDocument method is responsible for both file handling and HTML generation (line 32) +- Magic numbers used for port and feedback.txt file path (line 15, 39) +- Unused console.log statement (line 19) +- Inconsistent spacing and indentation (varies) +- No error handling for file handling operations (line 32, 43) +- Potential for infinite loop if feedback-server.ts is included in feedback.txt file (line 42) +- Lack of documentation for some code blocks (varies) + +Recommendations: +- Use consistent naming conventions for variables and classes, either fileHandler or FileHandlers (line 4, 6) +- Separate file handling and HTML generation into separate methods or classes for better modularity and separation of concerns (line 32) +- Use constants or environment variables instead of magic numbers for port and file paths (line 15, 39) +- Remove unused console.log statement (line 19) +- Use consistent spacing and indentation for better readability (varies) +- Implement error handling for file handling operations, such as try-catch blocks (line 32, 43) +- Add a check to prevent infinite loop if feedback-server.ts is included in feedback.txt file (line 42) +- Add more documentation for code blocks that may be unclear (varies) + +Rewrite: +- Separation of concerns can be achieved by creating a FeedbackGenerator class with methods for file handling and HTML generation. +- Use constants or environment variables for port and file paths. +- Implement error handling with try-catch blocks for file handling operations. +- Add comments or JSDoc for unclear code blocks. + + \ No newline at end of file diff --git a/index.ts b/index.ts index c1023b1..5f99f13 100644 --- a/index.ts +++ b/index.ts @@ -1,6 +1,7 @@ #!/usr/bin/env node import { ConfigStore } from './src/config'; +import { FeedbackServer } from './src/feedback-server'; import { AutoReviewer } from './src/index'; import { Utils } from './src/utils'; @@ -8,8 +9,9 @@ const chalk = require('chalk'); - -// Show the welcome message to the user +/** + * Displays a welcome message to the user + */ const showWelcomeMessage = () => { const welcomeText = ` ======================================================= @@ -32,26 +34,38 @@ showWelcomeMessage(); const autoReviewer = new AutoReviewer(); const config = new ConfigStore(); +const feedbackServer = new FeedbackServer(); + if (config.get('openai_api_key')) { - autoReviewer.codeReview().then(async () => { - const score = await autoReviewer.getScore(); - Utils.sleep(1000); - Utils.log(chalk.greenBright.bold('Code Review Completed successfully ☑️')); - Utils.log(chalk.greenBright.bold(`Your code score is: ${score} / 10`)); - - process.exit(0); - }); + (async () => { + try { + await autoReviewer.codeReview() + const score = await autoReviewer.getScore(); + Utils.sleep(1000); + Utils.log(chalk.greenBright.bold('Code Review Completed successfully ☑️')); + Utils.log(chalk.greenBright.bold(`Your code score is: ${score} / 10`)); + Utils.log(chalk.greenBright.bold(`Please visit http://localhost:3123 to view your feedback`)); + return feedbackServer.init(); + } catch (error: any) { + Utils.log(chalk.red.bold(`Error occurred: ${error.message}`)); + } + })(); } else { - config.promptUserForApiKey().then(() => { - Utils.sleep(1000); - Utils.log(chalk.greenBright.bold(`API Key saved successfully ☑️ - - Please run the command again to start the code review + (async () => { + try { + await config.promptUserForApiKey() + Utils.sleep(1000); + Utils.log(chalk.greenBright.bold(`API Key saved successfully ☑️ + + Please run the command again to start the code review `)); - process.exit(0); - }) + process.exit(0); + } catch (error: any) { + Utils.log(chalk.red.bold(`Error occurred: ${error.message}`)); + } + })(); } diff --git a/package-lock.json b/package-lock.json index 9198852..77788d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "1.0.5", "license": "ISC", "dependencies": { + "@hapi/hapi": "^21.3.2", + "@hapi/inert": "^7.1.0", "axios": "^1.3.4", "chalk": "4.1.0", "configstore": "5.0.0", @@ -17,6 +19,7 @@ "inquirer": "8.1.5", "openai": "^3.2.1", "simple-git": "^3.17.0", + "snarkdown": "^2.0.0", "yargs": "^17.7.1" }, "bin": { @@ -73,6 +76,306 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@hapi/accept": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/accept/-/accept-6.0.1.tgz", + "integrity": "sha512-aLkYj7zzgC3CSlEVOs84eBOEE3i9xZK2tdQEP+TOj2OFzMWCi9zjkRet82V3GGjecE//zFrCLKIykuaE0uM4bg==", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/ammo": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/ammo/-/ammo-6.0.1.tgz", + "integrity": "sha512-pmL+nPod4g58kXrMcsGLp05O2jF4P2Q3GiL8qYV7nKYEh3cGf+rV4P5Jyi2Uq0agGhVU63GtaSAfBEZOlrJn9w==", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/b64": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/b64/-/b64-6.0.1.tgz", + "integrity": "sha512-ZvjX4JQReUmBheeCq+S9YavcnMMHWqx3S0jHNXWIM1kQDxB9cyfSycpVvjfrKcIS8Mh5N3hmu/YKo4Iag9g2Kw==", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/boom": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-10.0.1.tgz", + "integrity": "sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/bounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@hapi/bounce/-/bounce-3.0.1.tgz", + "integrity": "sha512-G+/Pp9c1Ha4FDP+3Sy/Xwg2O4Ahaw3lIZFSX+BL4uWi64CmiETuZPxhKDUD4xBMOUZbBlzvO8HjiK8ePnhBadA==", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/bourne": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz", + "integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==" + }, + "node_modules/@hapi/call": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@hapi/call/-/call-9.0.1.tgz", + "integrity": "sha512-uPojQRqEL1GRZR4xXPqcLMujQGaEpyVPRyBlD8Pp5rqgIwLhtveF9PkixiKru2THXvuN8mUrLeet5fqxKAAMGg==", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/catbox": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@hapi/catbox/-/catbox-12.1.1.tgz", + "integrity": "sha512-hDqYB1J+R0HtZg4iPH3LEnldoaBsar6bYp0EonBmNQ9t5CO+1CqgCul2ZtFveW1ReA5SQuze9GPSU7/aecERhw==", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/podium": "^5.0.0", + "@hapi/validate": "^2.0.1" + } + }, + "node_modules/@hapi/catbox-memory": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/catbox-memory/-/catbox-memory-6.0.1.tgz", + "integrity": "sha512-sVb+/ZxbZIvaMtJfAbdyY+QJUQg9oKTwamXpEg/5xnfG5WbJLTjvEn4kIGKz9pN3ENNbIL/bIdctmHmqi/AdGA==", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/content": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@hapi/content/-/content-6.0.0.tgz", + "integrity": "sha512-CEhs7j+H0iQffKfe5Htdak5LBOz/Qc8TRh51cF+BFv0qnuph3Em4pjGVzJMkI2gfTDdlJKWJISGWS1rK34POGA==", + "dependencies": { + "@hapi/boom": "^10.0.0" + } + }, + "node_modules/@hapi/cryptiles": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/cryptiles/-/cryptiles-6.0.1.tgz", + "integrity": "sha512-9GM9ECEHfR8lk5ASOKG4+4ZsEzFqLfhiryIJ2ISePVB92OHLp/yne4m+zn7z9dgvM98TLpiFebjDFQ0UHcqxXQ==", + "dependencies": { + "@hapi/boom": "^10.0.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/file": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@hapi/file/-/file-3.0.0.tgz", + "integrity": "sha512-w+lKW+yRrLhJu620jT3y+5g2mHqnKfepreykvdOcl9/6up8GrQQn+l3FRTsjHTKbkbfQFkuksHpdv2EcpKcJ4Q==" + }, + "node_modules/@hapi/hapi": { + "version": "21.3.2", + "resolved": "https://registry.npmjs.org/@hapi/hapi/-/hapi-21.3.2.tgz", + "integrity": "sha512-tbm0zmsdUj8iw4NzFV30FST/W4qzh/Lsw6Q5o5gAhOuoirWvxm8a4G3o60bqBw8nXvRNJ8uLtE0RKLlZINxHcQ==", + "dependencies": { + "@hapi/accept": "^6.0.1", + "@hapi/ammo": "^6.0.1", + "@hapi/boom": "^10.0.1", + "@hapi/bounce": "^3.0.1", + "@hapi/call": "^9.0.1", + "@hapi/catbox": "^12.1.1", + "@hapi/catbox-memory": "^6.0.1", + "@hapi/heavy": "^8.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/mimos": "^7.0.1", + "@hapi/podium": "^5.0.1", + "@hapi/shot": "^6.0.1", + "@hapi/somever": "^4.1.1", + "@hapi/statehood": "^8.1.1", + "@hapi/subtext": "^8.1.0", + "@hapi/teamwork": "^6.0.0", + "@hapi/topo": "^6.0.1", + "@hapi/validate": "^2.0.1" + }, + "engines": { + "node": ">=14.15.0" + } + }, + "node_modules/@hapi/heavy": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@hapi/heavy/-/heavy-8.0.1.tgz", + "integrity": "sha512-gBD/NANosNCOp6RsYTsjo2vhr5eYA3BEuogk6cxY0QdhllkkTaJFYtTXv46xd6qhBVMbMMqcSdtqey+UQU3//w==", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/validate": "^2.0.1" + } + }, + "node_modules/@hapi/hoek": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.2.tgz", + "integrity": "sha512-aKmlCO57XFZ26wso4rJsW4oTUnrgTFw2jh3io7CAtO9w4UltBNwRXvXIVzzyfkaaLRo3nluP/19msA8vDUUuKw==" + }, + "node_modules/@hapi/inert": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@hapi/inert/-/inert-7.1.0.tgz", + "integrity": "sha512-5X+cl/Ozm0U9uPGGX1dSKhnhTQIf161bH/kkTN9OBVAZKFG+nrj8j/NMj6S1zBBZWmQrkVRNPfCUGrXzB4fCFQ==", + "dependencies": { + "@hapi/ammo": "^6.0.1", + "@hapi/boom": "^10.0.1", + "@hapi/bounce": "^3.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/validate": "^2.0.1", + "lru-cache": "^7.14.1" + } + }, + "node_modules/@hapi/iron": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@hapi/iron/-/iron-7.0.1.tgz", + "integrity": "sha512-tEZnrOujKpS6jLKliyWBl3A9PaE+ppuL/+gkbyPPDb/l2KSKQyH4lhMkVb+sBhwN+qaxxlig01JRqB8dk/mPxQ==", + "dependencies": { + "@hapi/b64": "^6.0.1", + "@hapi/boom": "^10.0.1", + "@hapi/bourne": "^3.0.0", + "@hapi/cryptiles": "^6.0.1", + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/mimos": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@hapi/mimos/-/mimos-7.0.1.tgz", + "integrity": "sha512-b79V+BrG0gJ9zcRx1VGcCI6r6GEzzZUgiGEJVoq5gwzuB2Ig9Cax8dUuBauQCFKvl2YWSWyOc8mZ8HDaJOtkew==", + "dependencies": { + "@hapi/hoek": "^11.0.2", + "mime-db": "^1.52.0" + } + }, + "node_modules/@hapi/nigel": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hapi/nigel/-/nigel-5.0.1.tgz", + "integrity": "sha512-uv3dtYuB4IsNaha+tigWmN8mQw/O9Qzl5U26Gm4ZcJVtDdB1AVJOwX3X5wOX+A07qzpEZnOMBAm8jjSqGsU6Nw==", + "dependencies": { + "@hapi/hoek": "^11.0.2", + "@hapi/vise": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/pez": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@hapi/pez/-/pez-6.1.0.tgz", + "integrity": "sha512-+FE3sFPYuXCpuVeHQ/Qag1b45clR2o54QoonE/gKHv9gukxQ8oJJZPR7o3/ydDTK6racnCJXxOyT1T93FCJMIg==", + "dependencies": { + "@hapi/b64": "^6.0.1", + "@hapi/boom": "^10.0.1", + "@hapi/content": "^6.0.0", + "@hapi/hoek": "^11.0.2", + "@hapi/nigel": "^5.0.1" + } + }, + "node_modules/@hapi/podium": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hapi/podium/-/podium-5.0.1.tgz", + "integrity": "sha512-eznFTw6rdBhAijXFIlBOMJJd+lXTvqbrBIS4Iu80r2KTVIo4g+7fLy4NKp/8+UnSt5Ox6mJtAlKBU/Sf5080TQ==", + "dependencies": { + "@hapi/hoek": "^11.0.2", + "@hapi/teamwork": "^6.0.0", + "@hapi/validate": "^2.0.1" + } + }, + "node_modules/@hapi/shot": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/shot/-/shot-6.0.1.tgz", + "integrity": "sha512-s5ynMKZXYoDd3dqPw5YTvOR/vjHvMTxc388+0qL0jZZP1+uwXuUD32o9DuuuLsmTlyXCWi02BJl1pBpwRuUrNA==", + "dependencies": { + "@hapi/hoek": "^11.0.2", + "@hapi/validate": "^2.0.1" + } + }, + "node_modules/@hapi/somever": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@hapi/somever/-/somever-4.1.1.tgz", + "integrity": "sha512-lt3QQiDDOVRatS0ionFDNrDIv4eXz58IibQaZQDOg4DqqdNme8oa0iPWcE0+hkq/KTeBCPtEOjDOBKBKwDumVg==", + "dependencies": { + "@hapi/bounce": "^3.0.1", + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/statehood": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@hapi/statehood/-/statehood-8.1.1.tgz", + "integrity": "sha512-YbK7PSVUA59NArAW5Np0tKRoIZ5VNYUicOk7uJmWZF6XyH5gGL+k62w77SIJb0AoAJ0QdGQMCQ/WOGL1S3Ydow==", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/bounce": "^3.0.1", + "@hapi/bourne": "^3.0.0", + "@hapi/cryptiles": "^6.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/iron": "^7.0.1", + "@hapi/validate": "^2.0.1" + } + }, + "node_modules/@hapi/subtext": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@hapi/subtext/-/subtext-8.1.0.tgz", + "integrity": "sha512-PyaN4oSMtqPjjVxLny1k0iYg4+fwGusIhaom9B2StinBclHs7v46mIW706Y+Wo21lcgulGyXbQrmT/w4dus6ww==", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/bourne": "^3.0.0", + "@hapi/content": "^6.0.0", + "@hapi/file": "^3.0.0", + "@hapi/hoek": "^11.0.2", + "@hapi/pez": "^6.1.0", + "@hapi/wreck": "^18.0.1" + } + }, + "node_modules/@hapi/teamwork": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@hapi/teamwork/-/teamwork-6.0.0.tgz", + "integrity": "sha512-05HumSy3LWfXpmJ9cr6HzwhAavrHkJ1ZRCmNE2qJMihdM5YcWreWPfyN0yKT2ZjCM92au3ZkuodjBxOibxM67A==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/topo": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz", + "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/validate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/validate/-/validate-2.0.1.tgz", + "integrity": "sha512-NZmXRnrSLK8MQ9y/CMqE9WSspgB9xA41/LlYR0k967aSZebWr4yNrpxIbov12ICwKy4APSlWXZga9jN5p6puPA==", + "dependencies": { + "@hapi/hoek": "^11.0.2", + "@hapi/topo": "^6.0.1" + } + }, + "node_modules/@hapi/vise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hapi/vise/-/vise-5.0.1.tgz", + "integrity": "sha512-XZYWzzRtINQLedPYlIkSkUr7m5Ddwlu99V9elh8CSygXstfv3UnWIXT0QD+wmR0VAG34d2Vx3olqcEhRRoTu9A==", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/wreck": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@hapi/wreck/-/wreck-18.0.1.tgz", + "integrity": "sha512-OLHER70+rZxvDl75xq3xXOfd3e8XIvz8fWY0dqg92UvhZ29zo24vQgfqgHSYhB5ZiuFpSLeriOisAlxAo/1jWg==", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/bourne": "^3.0.0", + "@hapi/hoek": "^11.0.2" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -1486,6 +1789,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -2007,6 +2318,11 @@ "url": "https://github.com/steveukx/git-js?sponsor=1" } }, + "node_modules/snarkdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/snarkdown/-/snarkdown-2.0.0.tgz", + "integrity": "sha512-MgL/7k/AZdXCTJiNgrO7chgDqaB9FGM/1Tvlcenenb7div6obaDATzs16JhFyHHBGodHT3B7RzRc5qk8pFhg3A==" + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -2504,6 +2820,294 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.35.0.tgz", "integrity": "sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==" }, + "@hapi/accept": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/accept/-/accept-6.0.1.tgz", + "integrity": "sha512-aLkYj7zzgC3CSlEVOs84eBOEE3i9xZK2tdQEP+TOj2OFzMWCi9zjkRet82V3GGjecE//zFrCLKIykuaE0uM4bg==", + "requires": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2" + } + }, + "@hapi/ammo": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/ammo/-/ammo-6.0.1.tgz", + "integrity": "sha512-pmL+nPod4g58kXrMcsGLp05O2jF4P2Q3GiL8qYV7nKYEh3cGf+rV4P5Jyi2Uq0agGhVU63GtaSAfBEZOlrJn9w==", + "requires": { + "@hapi/hoek": "^11.0.2" + } + }, + "@hapi/b64": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/b64/-/b64-6.0.1.tgz", + "integrity": "sha512-ZvjX4JQReUmBheeCq+S9YavcnMMHWqx3S0jHNXWIM1kQDxB9cyfSycpVvjfrKcIS8Mh5N3hmu/YKo4Iag9g2Kw==", + "requires": { + "@hapi/hoek": "^11.0.2" + } + }, + "@hapi/boom": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-10.0.1.tgz", + "integrity": "sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==", + "requires": { + "@hapi/hoek": "^11.0.2" + } + }, + "@hapi/bounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@hapi/bounce/-/bounce-3.0.1.tgz", + "integrity": "sha512-G+/Pp9c1Ha4FDP+3Sy/Xwg2O4Ahaw3lIZFSX+BL4uWi64CmiETuZPxhKDUD4xBMOUZbBlzvO8HjiK8ePnhBadA==", + "requires": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2" + } + }, + "@hapi/bourne": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz", + "integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==" + }, + "@hapi/call": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@hapi/call/-/call-9.0.1.tgz", + "integrity": "sha512-uPojQRqEL1GRZR4xXPqcLMujQGaEpyVPRyBlD8Pp5rqgIwLhtveF9PkixiKru2THXvuN8mUrLeet5fqxKAAMGg==", + "requires": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2" + } + }, + "@hapi/catbox": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@hapi/catbox/-/catbox-12.1.1.tgz", + "integrity": "sha512-hDqYB1J+R0HtZg4iPH3LEnldoaBsar6bYp0EonBmNQ9t5CO+1CqgCul2ZtFveW1ReA5SQuze9GPSU7/aecERhw==", + "requires": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/podium": "^5.0.0", + "@hapi/validate": "^2.0.1" + } + }, + "@hapi/catbox-memory": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/catbox-memory/-/catbox-memory-6.0.1.tgz", + "integrity": "sha512-sVb+/ZxbZIvaMtJfAbdyY+QJUQg9oKTwamXpEg/5xnfG5WbJLTjvEn4kIGKz9pN3ENNbIL/bIdctmHmqi/AdGA==", + "requires": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2" + } + }, + "@hapi/content": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@hapi/content/-/content-6.0.0.tgz", + "integrity": "sha512-CEhs7j+H0iQffKfe5Htdak5LBOz/Qc8TRh51cF+BFv0qnuph3Em4pjGVzJMkI2gfTDdlJKWJISGWS1rK34POGA==", + "requires": { + "@hapi/boom": "^10.0.0" + } + }, + "@hapi/cryptiles": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/cryptiles/-/cryptiles-6.0.1.tgz", + "integrity": "sha512-9GM9ECEHfR8lk5ASOKG4+4ZsEzFqLfhiryIJ2ISePVB92OHLp/yne4m+zn7z9dgvM98TLpiFebjDFQ0UHcqxXQ==", + "requires": { + "@hapi/boom": "^10.0.1" + } + }, + "@hapi/file": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@hapi/file/-/file-3.0.0.tgz", + "integrity": "sha512-w+lKW+yRrLhJu620jT3y+5g2mHqnKfepreykvdOcl9/6up8GrQQn+l3FRTsjHTKbkbfQFkuksHpdv2EcpKcJ4Q==" + }, + "@hapi/hapi": { + "version": "21.3.2", + "resolved": "https://registry.npmjs.org/@hapi/hapi/-/hapi-21.3.2.tgz", + "integrity": "sha512-tbm0zmsdUj8iw4NzFV30FST/W4qzh/Lsw6Q5o5gAhOuoirWvxm8a4G3o60bqBw8nXvRNJ8uLtE0RKLlZINxHcQ==", + "requires": { + "@hapi/accept": "^6.0.1", + "@hapi/ammo": "^6.0.1", + "@hapi/boom": "^10.0.1", + "@hapi/bounce": "^3.0.1", + "@hapi/call": "^9.0.1", + "@hapi/catbox": "^12.1.1", + "@hapi/catbox-memory": "^6.0.1", + "@hapi/heavy": "^8.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/mimos": "^7.0.1", + "@hapi/podium": "^5.0.1", + "@hapi/shot": "^6.0.1", + "@hapi/somever": "^4.1.1", + "@hapi/statehood": "^8.1.1", + "@hapi/subtext": "^8.1.0", + "@hapi/teamwork": "^6.0.0", + "@hapi/topo": "^6.0.1", + "@hapi/validate": "^2.0.1" + } + }, + "@hapi/heavy": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@hapi/heavy/-/heavy-8.0.1.tgz", + "integrity": "sha512-gBD/NANosNCOp6RsYTsjo2vhr5eYA3BEuogk6cxY0QdhllkkTaJFYtTXv46xd6qhBVMbMMqcSdtqey+UQU3//w==", + "requires": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/validate": "^2.0.1" + } + }, + "@hapi/hoek": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.2.tgz", + "integrity": "sha512-aKmlCO57XFZ26wso4rJsW4oTUnrgTFw2jh3io7CAtO9w4UltBNwRXvXIVzzyfkaaLRo3nluP/19msA8vDUUuKw==" + }, + "@hapi/inert": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@hapi/inert/-/inert-7.1.0.tgz", + "integrity": "sha512-5X+cl/Ozm0U9uPGGX1dSKhnhTQIf161bH/kkTN9OBVAZKFG+nrj8j/NMj6S1zBBZWmQrkVRNPfCUGrXzB4fCFQ==", + "requires": { + "@hapi/ammo": "^6.0.1", + "@hapi/boom": "^10.0.1", + "@hapi/bounce": "^3.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/validate": "^2.0.1", + "lru-cache": "^7.14.1" + } + }, + "@hapi/iron": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@hapi/iron/-/iron-7.0.1.tgz", + "integrity": "sha512-tEZnrOujKpS6jLKliyWBl3A9PaE+ppuL/+gkbyPPDb/l2KSKQyH4lhMkVb+sBhwN+qaxxlig01JRqB8dk/mPxQ==", + "requires": { + "@hapi/b64": "^6.0.1", + "@hapi/boom": "^10.0.1", + "@hapi/bourne": "^3.0.0", + "@hapi/cryptiles": "^6.0.1", + "@hapi/hoek": "^11.0.2" + } + }, + "@hapi/mimos": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@hapi/mimos/-/mimos-7.0.1.tgz", + "integrity": "sha512-b79V+BrG0gJ9zcRx1VGcCI6r6GEzzZUgiGEJVoq5gwzuB2Ig9Cax8dUuBauQCFKvl2YWSWyOc8mZ8HDaJOtkew==", + "requires": { + "@hapi/hoek": "^11.0.2", + "mime-db": "^1.52.0" + } + }, + "@hapi/nigel": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hapi/nigel/-/nigel-5.0.1.tgz", + "integrity": "sha512-uv3dtYuB4IsNaha+tigWmN8mQw/O9Qzl5U26Gm4ZcJVtDdB1AVJOwX3X5wOX+A07qzpEZnOMBAm8jjSqGsU6Nw==", + "requires": { + "@hapi/hoek": "^11.0.2", + "@hapi/vise": "^5.0.1" + } + }, + "@hapi/pez": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@hapi/pez/-/pez-6.1.0.tgz", + "integrity": "sha512-+FE3sFPYuXCpuVeHQ/Qag1b45clR2o54QoonE/gKHv9gukxQ8oJJZPR7o3/ydDTK6racnCJXxOyT1T93FCJMIg==", + "requires": { + "@hapi/b64": "^6.0.1", + "@hapi/boom": "^10.0.1", + "@hapi/content": "^6.0.0", + "@hapi/hoek": "^11.0.2", + "@hapi/nigel": "^5.0.1" + } + }, + "@hapi/podium": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hapi/podium/-/podium-5.0.1.tgz", + "integrity": "sha512-eznFTw6rdBhAijXFIlBOMJJd+lXTvqbrBIS4Iu80r2KTVIo4g+7fLy4NKp/8+UnSt5Ox6mJtAlKBU/Sf5080TQ==", + "requires": { + "@hapi/hoek": "^11.0.2", + "@hapi/teamwork": "^6.0.0", + "@hapi/validate": "^2.0.1" + } + }, + "@hapi/shot": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/shot/-/shot-6.0.1.tgz", + "integrity": "sha512-s5ynMKZXYoDd3dqPw5YTvOR/vjHvMTxc388+0qL0jZZP1+uwXuUD32o9DuuuLsmTlyXCWi02BJl1pBpwRuUrNA==", + "requires": { + "@hapi/hoek": "^11.0.2", + "@hapi/validate": "^2.0.1" + } + }, + "@hapi/somever": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@hapi/somever/-/somever-4.1.1.tgz", + "integrity": "sha512-lt3QQiDDOVRatS0ionFDNrDIv4eXz58IibQaZQDOg4DqqdNme8oa0iPWcE0+hkq/KTeBCPtEOjDOBKBKwDumVg==", + "requires": { + "@hapi/bounce": "^3.0.1", + "@hapi/hoek": "^11.0.2" + } + }, + "@hapi/statehood": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@hapi/statehood/-/statehood-8.1.1.tgz", + "integrity": "sha512-YbK7PSVUA59NArAW5Np0tKRoIZ5VNYUicOk7uJmWZF6XyH5gGL+k62w77SIJb0AoAJ0QdGQMCQ/WOGL1S3Ydow==", + "requires": { + "@hapi/boom": "^10.0.1", + "@hapi/bounce": "^3.0.1", + "@hapi/bourne": "^3.0.0", + "@hapi/cryptiles": "^6.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/iron": "^7.0.1", + "@hapi/validate": "^2.0.1" + } + }, + "@hapi/subtext": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@hapi/subtext/-/subtext-8.1.0.tgz", + "integrity": "sha512-PyaN4oSMtqPjjVxLny1k0iYg4+fwGusIhaom9B2StinBclHs7v46mIW706Y+Wo21lcgulGyXbQrmT/w4dus6ww==", + "requires": { + "@hapi/boom": "^10.0.1", + "@hapi/bourne": "^3.0.0", + "@hapi/content": "^6.0.0", + "@hapi/file": "^3.0.0", + "@hapi/hoek": "^11.0.2", + "@hapi/pez": "^6.1.0", + "@hapi/wreck": "^18.0.1" + } + }, + "@hapi/teamwork": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@hapi/teamwork/-/teamwork-6.0.0.tgz", + "integrity": "sha512-05HumSy3LWfXpmJ9cr6HzwhAavrHkJ1ZRCmNE2qJMihdM5YcWreWPfyN0yKT2ZjCM92au3ZkuodjBxOibxM67A==" + }, + "@hapi/topo": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz", + "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==", + "requires": { + "@hapi/hoek": "^11.0.2" + } + }, + "@hapi/validate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/validate/-/validate-2.0.1.tgz", + "integrity": "sha512-NZmXRnrSLK8MQ9y/CMqE9WSspgB9xA41/LlYR0k967aSZebWr4yNrpxIbov12ICwKy4APSlWXZga9jN5p6puPA==", + "requires": { + "@hapi/hoek": "^11.0.2", + "@hapi/topo": "^6.0.1" + } + }, + "@hapi/vise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hapi/vise/-/vise-5.0.1.tgz", + "integrity": "sha512-XZYWzzRtINQLedPYlIkSkUr7m5Ddwlu99V9elh8CSygXstfv3UnWIXT0QD+wmR0VAG34d2Vx3olqcEhRRoTu9A==", + "requires": { + "@hapi/hoek": "^11.0.2" + } + }, + "@hapi/wreck": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@hapi/wreck/-/wreck-18.0.1.tgz", + "integrity": "sha512-OLHER70+rZxvDl75xq3xXOfd3e8XIvz8fWY0dqg92UvhZ29zo24vQgfqgHSYhB5ZiuFpSLeriOisAlxAo/1jWg==", + "requires": { + "@hapi/boom": "^10.0.1", + "@hapi/bourne": "^3.0.0", + "@hapi/hoek": "^11.0.2" + } + }, "@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -3527,6 +4131,11 @@ "is-unicode-supported": "^0.1.0" } }, + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==" + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -3871,6 +4480,11 @@ "debug": "^4.3.4" } }, + "snarkdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/snarkdown/-/snarkdown-2.0.0.tgz", + "integrity": "sha512-MgL/7k/AZdXCTJiNgrO7chgDqaB9FGM/1Tvlcenenb7div6obaDATzs16JhFyHHBGodHT3B7RzRc5qk8pFhg3A==" + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/package.json b/package.json index 68dac35..cdcb906 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,8 @@ "typescript": "^4.9.5" }, "dependencies": { + "@hapi/hapi": "^21.3.2", + "@hapi/inert": "^7.1.0", "axios": "^1.3.4", "chalk": "4.1.0", "configstore": "5.0.0", @@ -45,4 +47,4 @@ "url": "https://github.com/bemijonathan/code-critique-ai/issues" }, "homepage": "https://github.com/bemijonathan/code-critique-ai#readme" -} +} \ No newline at end of file diff --git a/public/index.css b/public/index.css new file mode 100644 index 0000000..7590572 --- /dev/null +++ b/public/index.css @@ -0,0 +1,185 @@ +body { + font-family: 'Roboto', sans-serif; + color: #ddd; + background-color: #262626; + margin: 0; + padding: 0; +} + +header { + background-color: #1d1d1d; + color: #f5a623; + padding: 20px 0; + text-align: center; + font-size: 24px; + letter-spacing: 1px; +} + +section { + background-color: #1d1d1d; + margin: 20px auto; + padding: 30px; + border-radius: 5px; + max-width: 80%; +} + +h2 { + font-size: 22px; + border-bottom: 1px solid #444; + padding-bottom: 10px; + margin-bottom: 20px; + color: #f5a623; +} + +h3 { + font-size: 18px; + margin-bottom: 15px; + color: #f5a623; +} + +p { + font-size: 16px; + line-height: 1.6; + margin-bottom: 20px; +} + +code { + font-family: 'Courier New', monospace; + background-color: #262626; + padding: 5px; + border-radius: 3px; + display: block; + white-space: pre-wrap; + margin-bottom: 20px; + color: #ddd; + border: 1px solid #444; +} + +a { + color: #3498db; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +.editor { + height: 500px; + width: 100%; +} + + +/* body { + font-family: 'Roboto', sans-serif; + color: #444; + background-color: #f9f9f9; + margin: 0; + padding: 0; +} + +header { + background-color: #2c3e50; + color: #fff; + padding: 20px 0; + text-align: center; + font-size: 24px; + letter-spacing: 1px; +} + +section { + background-color: #fff; + margin: 20px auto; + padding: 30px; + border-radius: 5px; + box-shadow: 0px 0px 10px rgba(0,0,0,0.1); + max-width: 800px; +} + +h2 { + font-size: 22px; + border-bottom: 1px solid #eee; + padding-bottom: 10px; + margin-bottom: 20px; + color: #34495e; +} + +h3 { + font-size: 18px; + margin-bottom: 15px; + color: #2c3e50; +} + +p { + font-size: 16px; + line-height: 1.6; + margin-bottom: 20px; +} + +code { + font-family: 'Courier New', monospace; + background-color: #f0f0f0; + padding: 5px; + border-radius: 3px; + display: block; + white-space: pre-wrap; + margin-bottom: 20px; +} + +a { + color: #3498db; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} */ + + +/* body { + font-family: Arial, sans-serif; + color: #333; + background-color: #f6f6f6; + margin: 0; + padding: 0; +} + +header { + background-color: #333; + color: #fff; + padding: 10px 0; + text-align: center; +} + +section { + background-color: #fff; + margin: 15px; + padding: 20px; + border-radius: 5px; + box-shadow: 0px 0px 10px rgba(0,0,0,0.1); +} + +h2 { + font-size: 20px; + border-bottom: 1px solid #ddd; + padding-bottom: 10px; + margin-bottom: 15px; +} + +h3 { + font-size: 18px; + margin-bottom: 10px; +} + +p { + font-size: 16px; + line-height: 1.6; + margin-bottom: 20px; +} + +code { + font-family: 'Courier New', monospace; + background-color: #f0f0f0; + padding: 2px 4px; + border-radius: 3px; +} */ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..46cf905 --- /dev/null +++ b/public/index.html @@ -0,0 +1,858 @@ + + + + + + + + + + + + Code Review + + +
+

Code Reviews

+ ❗️❗️❗️ +

+ Note: This review is AI-generated 🤖. Please critically consider applying only the information that resonates with you 💡. +

+
+ +
+

+

+ +

Criticism

+

No criticism provided for review.

+

Recommendations

+

undefined

+
+ +
+

index.ts +

+ /Users/jonathan_atiene/dev/code-critique-ai/index.ts +

Criticism

+

+ Issues:
- Inconsistent naming conventions (e.g. FeedBackServer vs FeedbackServer)
- Unused variable (config) on line 18
- Redundant sleep function call on line 15
- Lack of error handling for codeReview and getScore functions
- No documentation or comments for functions and classes

+

Recommendations

+

Recommendations:
- Consistently use camelCase naming convention for variables and classes
- Remove unused variable on line 18
- Remove redundant sleep function call on line 15
- Implement try-catch blocks for codeReview and getScore functions to handle errors
- Add documentation and comments for functions and classes to improve readability and maintainability

+

Rewrite

+ Rewrite: + +#!/usr/bin/env node + +import { ConfigStore } from './src/config'; +import { FeedbackServer } from './src/feedback-server'; +import { AutoReviewer } from './src/index'; +import { Utils } from './src/utils'; + +const chalk = require('chalk'); + +/** + * Displays a welcome message to the user + */ +const showWelcomeMessage = () => { + const welcomeText = ` + ======================================================= + + Welcome to AutoReviewer - Your Code Review Helper + + ======================================================= + + 1. AutoReviewer will review your code and provide you with feedback + 2. AutoReviewer will also provide you with a score for your code review + + ------------------------------------------------------- + `; + Utils.log(chalk.greenBright.bold(welcomeText)); +} + +showWelcomeMessage(); + +const autoReviewer = new AutoReviewer(); +const feedbackServer = new FeedbackServer(); + +if (autoReviewer.openaiApiKey) { + (async () => { + try { + await autoReviewer.codeReview(); + const score = await autoReviewer.getScore(); + Utils.log(chalk.greenBright.bold('Code Review Completed successfully ☑️')); + Utils.log(chalk.greenBright.bold(`Your code score is: ${score} / 10`)); + Utils.log(chalk.greenBright.bold(`Please visit http://localhost:3123 to view your feedback`)); + await feedbackServer.init(); + } catch (error) { + Utils.log(chalk.red.bold(`Error occurred: ${error.message}`)); + } + })(); +} else { + (async () => { + try { + await new ConfigStore().promptUserForApiKey(); + Utils.log(chalk.greenBright.bold(`API Key saved successfully ☑️ + + Please run the command again to start the code review + + `)); + } catch (error) { + Utils.log(chalk.red.bold(`Error occurred: ${error.message}`)); + } + })(); +} + +Changes Made: +- Renamed FeedbackServer to feedbackServer for consistent naming convention +- Removed unused variable config on line 18 +- Removed redundant sleep function call on line 15 +- Added try-catch blocks for codeReview and getScore functions to handle errors +- Added JSDoc comments for showWelcomeMessage function +- Added inline comments for code clarity +
+ #!/usr/bin/env node + +import { ConfigStore } from './src/config'; +import { FeedbackServer } from './src/feedback-server'; +import { AutoReviewer } from './src/index'; +import { Utils } from './src/utils'; + +const chalk = require('chalk'); + + + +/** + * Displays a welcome message to the user + */ +const showWelcomeMessage = () => { + const welcomeText = ` + ======================================================= + + Welcome to AutoReviewer - Your Code Review Helper + + ======================================================= + + 1. AutoReviewer will review your code and provide you with feedback + 2. AutoReviewer will also provide you with a score for your code review + + ------------------------------------------------------- + `; + Utils.log(chalk.greenBright.bold(welcomeText)); +} + + +showWelcomeMessage(); + + +const autoReviewer = new AutoReviewer(); +const config = new ConfigStore(); +const feedbackServer = new FeedbackServer(); + + +if (config.get('openai_api_key')) { + (async () => { + try { + // await autoReviewer.codeReview() + // const score = await autoReviewer.getScore(); + // Utils.sleep(1000); + // Utils.log(chalk.greenBright.bold('Code Review Completed successfully ☑️')); + // Utils.log(chalk.greenBright.bold(`Your code score is: ${score} / 10`)); + // Utils.log(chalk.greenBright.bold(`Please visit http://localhost:3123 to view your feedback`)); + return feedbackServer.init(); + } catch (error: any) { + // Utils.log(chalk.red.bold(`Error occurred: ${error.message}`)); + } + })(); +} else { + (async () => { + try { + await config.promptUserForApiKey() + Utils.sleep(1000); + Utils.log(chalk.greenBright.bold(`API Key saved successfully ☑️ + + Please run the command again to start the code review + + `)); + process.exit(0); + } catch (error: any) { + Utils.log(chalk.red.bold(`Error occurred: ${error.message}`)); + } + })(); +} + + + + + + + + + +

ts

+
+
+ +
+

index.js +

+ /Users/jonathan_atiene/dev/code-critique-ai/public/index.js +

Criticism

+

+ Issues:
- Lack of documentation (no comments or explanations)
- No error handling for require.config (line 1)
- Inconsistent variable naming (e.g. codeElements vs. initialCodes)
- Unused variable "diffEditor" (line 21)
- Redundant code in forEach loop (lines 29-34)
- Inefficient use of querySelectorAll (could use getElementById instead)
- No interface or type definitions for objects or data structures

+

Recommendations

+

Recommendations:
- Add comments to explain code functionality and purpose
- Add error handling for require.config in case of network failure
- Use consistent naming conventions (e.g. use "codeElements" consistently instead of switching to "div")
- Remove unused variable "diffEditor"
- Refactor forEach loop to avoid redundant code and improve readability
- Use getElementById instead of querySelectorAll for improved performance
- Define interfaces or types for objects and data structures to improve code robustness and maintainability

+

Rewrite

+ Rewrite: + +// Define paths for external libraries +require.config({ + paths: { + 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs' + } +}); + +// Define mapping of file extensions to programming languages +const extensionToLanguage = { + 'js': 'javascript', + 'py': 'python', + 'java': 'java', + 'ts': 'typescript', + 'tsx': 'typescript', + 'jsx': 'javascript', + // Add more mappings as needed +}; + +// Load Monaco editor library and initialize diff editors for each code element +require(['vs/editor/editor.main'], function() { + // Get all code elements and loop through them + const codeElements = document.querySelectorAll('code'); + codeElements.forEach((codeElement, index) => { + // Create a new div element to replace the code element + const div = document.createElement('div'); + div.id = `editor${index}`; + div.className = 'editor'; + codeElement.parentNode.replaceChild(div, codeElement); + + // Get the original and modified code from the DOM + const initialCode = document.querySelector(`.original:nth-child(${index + 1})`).textContent; + const modifiedCode = codeElement.textContent; + const language = document.querySelector(`.original:nth-child(${index + 1}) p`).textContent; + + // Create a new Monaco DiffEditor instance in the div + const diffEditor = monaco.editor.createDiffEditor(document.getElementById(div.id), { + theme: 'vs-dark', + }); + diffEditor.setModel({ + original: monaco.editor.createModel(initialCode, extensionToLanguage[language]), + modified: monaco.editor.createModel(modifiedCode, extensionToLanguage[language]) + }); + + // Remove the DOM elements of the original code and language + const initialCodeElement = document.querySelector(`.original:nth-child(${index + 1})`); + const languageElement = initialCodeElement.querySelector('p'); + initialCodeElement.parentNode.removeChild(initialCodeElement); + languageElement.parentNode.removeChild(languageElement); + }); +}); + +The rewritten code addresses the issues listed above and improves readability, maintainability, and performance. +
+ + + require.config({ + paths: { + 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs' + } + }); + + + let extensionToLanguage = { + 'js': 'javascript', + 'py': 'python', + 'java': 'java', + 'ts': 'typescript', + 'tsx': 'typescript', + 'jsx': 'javascript', + // Add more mappings as needed + }; + + + + require(['vs/editor/editor.main'], function() { + let codeElements = document.querySelectorAll('code'); + let initialCodes = document.querySelectorAll('.original'); + let languages = document.querySelectorAll('.original p'); + + console.log(codeElements.length, initialCodes.length, languages.length); + + codeElements.forEach((codeElement, index) => { + // create a new div element + let div = document.createElement('div'); + div.id = 'editor' + index; + div.className = 'editor'; + + // replace the code element with the div + codeElement.parentNode.replaceChild(div, codeElement); + + // create a Monaco DiffEditor instance in the div + var diffEditor = monaco.editor.createDiffEditor(document.getElementById(div.id), { + theme: 'vs-dark', + }); + + // the original and modified code + const language = languages[index].textContent; + var originalCode = initialCodes[index].textContent; // replace this with the original code + var modifiedCode = codeElement.textContent; + + diffEditor.setModel({ + original: monaco.editor.createModel(originalCode, extensionToLanguage[language]), + modified: monaco.editor.createModel(modifiedCode, extensionToLanguage[language]) + }); + + // After creating the diffEditor, remove the DOM element of the original code + // This will prevent indexing issues by ensuring we're done using the arrays for this iteration before modifying them + initialCodes[index].parentNode.removeChild(initialCodes[index]); + languages[index].parentNode.removeChild(languages[index]); + }); + + }); + +

js

+
+
+ +
+

code-review.ts +

+ /Users/jonathan_atiene/dev/code-critique-ai/src/code-review.ts +

Criticism

+

+ Issues:
- Lack of documentation for functions and methods
- Inconsistent naming conventions for variables (e.g. feedbacks vs. scores)
- Code duplication in getCodeReview method (repeated code for handling codeReview)
- No error handling for some methods (e.g. getFilesContent)

+

Recommendations

+

Recommendations:
- Add JSDoc comments to functions and methods to improve readability and maintainability
- Use consistent naming conventions for variables (e.g. use "feedbacks" instead of "scores" for consistency with class property)
- Refactor getCodeReview method to remove code duplication and improve maintainability
- Implement error handling for all methods to improve robustness and prevent unexpected errors

+
+ +
+

config.ts +

+ /Users/jonathan_atiene/dev/code-critique-ai/src/config.ts +

Criticism

+

+ Issues:
- Inconsistent naming conventions: the class name is in PascalCase, while the method and variable names are in camelCase (line 4, 6, 8, 10, 12, 14)
- Redundant type definition for _config variable (line 6)
- Magic string for the config store name (line 8)
- Lack of error handling for config store initialization (line 10)
- Inconsistent quotes used in inquirer prompt definition (line 17)
- Magic string for the openai api key name (line 18)
- Lack of error handling for setting config values (line 20)
- No validation for the openai api key input (line 16-22)

+

Recommendations

+

Recommendations:
- Use consistent naming conventions for all elements in the codebase (e.g. camelCase for variables, PascalCase for classes)
- Remove the type definition for _config variable as it can be inferred from the constructor
- Define a constant variable for the config store name instead of using a magic string
- Add try-catch block for config store initialization to handle errors
- Use consistent quotes (preferably single quotes) for all strings in the codebase
- Define a constant variable for the openai api key name instead of using a magic string
- Add validation for the openai api key input (e.g. check for length, format)
- Add try-catch block for setting config values to handle errors

+

Rewrite

+ Rewrite: + +import Configstore from 'configstore'; +import Inquirer from 'inquirer'; + +const CONFIG_STORE_NAME = 'auto-reviewer'; +const OPENAI_API_KEY_NAME = 'openai_api_key'; + +export class ConfigStore { + private config: Configstore; + + constructor() { + try { + this.config = new Configstore(CONFIG_STORE_NAME); + } catch (error) { + console.error('Error initializing config store:', error); + process.exit(1); + } + } + + get(key: string) { + return this.config.get(key); + } + + set(key: string, value: any) { + try { + this.config.set(key, value); + } catch (error) { + console.error('Error setting config value:', error); + process.exit(1); + } + } + + async promptUserForApiKey() { + const answer = await Inquirer.prompt([{ + type: 'input', + name: OPENAI_API_KEY_NAME, + message: 'Please enter your openai api key', + validate: (input: string) => { + if (input.length < 1) { + return 'API key cannot be empty'; + } + // add more validation as needed + return true; + }, + }]); + this.set(OPENAI_API_KEY_NAME, answer[OPENAI_API_KEY_NAME]); + return answer; + } +} + +
+ const config = require('configstore') +const inquirer = require('inquirer'); + +export class ConfigStore { + + private _config: typeof config; + + constructor() { + this._config = new config('auto-reviewer') + } + + get(key: string) { + return this._config.get(key); + } + + set(key: string, value: any) { + this._config.set(key, value); + } + + async promptUserForApiKey() { + const answer = await inquirer.prompt([{ + 'type': 'input', + 'name': 'openai_api_key', + 'message': 'Please enter your openai api key' + }]) + this.set('openai_api_key', answer.openai_api_key); + return answer; + } +} +

ts

+
+
+ +
+

filehandler.ts +

+ /Users/jonathan_atiene/dev/code-critique-ai/src/filehandler.ts +

Criticism

+

+ Issues:
- Inconsistent naming conventions. The function getProjectConfig uses camelCase while the file codereview.json uses snake_case. (line 9)
- The default config object in getProjectConfig is hardcoded and not flexible. (line 14)
- The getFilesInDirectory function has a side effect of modifying the projectFiles array outside of its scope. (line 22)
- The function getFileContent throws an error but does not provide any information about the error. (line 35)
- The writeFileContent function does not handle errors. (line 42)
- The delete function does not handle errors. (line 50)

+

Recommendations

+

Recommendations:
- Use consistent naming conventions throughout the codebase. Either use camelCase or snake_case for all variables, functions, and files.
- Make the default config object more flexible by allowing it to be passed as an argument or read from a separate config file.
- Refactor the getFilesInDirectory function to not modify the projectFiles array outside of its scope. Instead, return the array as a value.
- Improve the error handling in getFileContent by providing more information about the error, such as the type of error and the file path.
- Implement error handling in the writeFileContent function by using try-catch blocks to catch any errors that may occur.
- Implement error handling in the delete function by using try-catch blocks to catch any errors that may occur.

+

Rewrite

+ Rewrite: + +import * as fs from 'fs'; +import { Utils } from './utils'; +import { ProjectConfig } from '../types'; + +export class FileHandlers { + private cwd = process.cwd(); + private projectFiles: string[] = []; + + public async getProjectConfig(): Promise { + const configFilePath = `${this.cwd}/codereview.json`; + + if (fs.existsSync(configFilePath)) { + const { content } = await this.getFileContent(configFilePath); + return JSON.parse(content.toString()); + } + + return { + name: 'ai-code-review', + description: 'ai-code-review', + git: false, + defaultBranch: 'master', + }; + } + + @Utils.catchError + public getFilesInDirectory(extensions: string[], projectPath?: string): string[] { + const path = projectPath ? projectPath : this.cwd; + const files = fs.readdirSync(path); + const fileArray: string[] = []; + + files.forEach((file) => { + if (!(file === 'node_modules' || file.startsWith('.'))) { + const filePath = `${path}/${file}`; + const isDirectory = fs.lstatSync(filePath).isDirectory(); + const isSupportedFile = extensions.some((extension) => file.endsWith(extension)); + + if (isDirectory) { + const subFiles = this.getFilesInDirectory(extensions, filePath); + fileArray.push(...subFiles); + } else if (isSupportedFile) { + fileArray.push(filePath); + } + } + }); + + console.log(`Found ${fileArray.length} files in directory: ${path}`); + + return fileArray; + } + + @Utils.catchError + public getFileContent(path: string): { content: string; path: string } { + if (fs.existsSync(path)) { + return { + content: fs.readFileSync(path, 'utf8'), + path, + }; + } + + throw new Error(`File ${path} does not exist`); + } + + @Utils.catchError + public writeFileContent(path: string, content: string): void { + try { + if (!fs.existsSync(path)) { + fs.writeFileSync(path, content, 'utf8'); + } else { + fs.appendFileSync(path, content, 'utf8'); + } + } catch (error) { + console.error(`Error writing to file ${path}: ${error.message}`); + } + } + + @Utils.catchError + public delete(path: string): void { + try { + if (fs.existsSync(path)) { + fs.unlinkSync(path); + } + } catch (error) { + console.error(`Error deleting file ${path}: ${error.message}`); + } + } +} + +
+ import * as fs from 'fs'; +import { Utils } from './utils'; +import { ProjectConfig } from '../types'; +export class FileHandlers { + cwd = process.cwd(); + projectFiles: string[] = []; + + + async getProjectConfig() { + // if there is a code-review.json file in the project then we should use that + // if not then we should return a default config + if (fs.existsSync(`${this.cwd}/codereview.json`)) { + const { content } = await this.getFileContent(`${this.cwd}/codereview.json`); + return JSON.parse(content.toString()); + } + return { + name: 'ai-code-review', + description: 'ai-code-review', + git: false, + defaultBranch: 'master', + } + } + + @Utils.catchError + getFilesInDirectory(extensions: string[], projectPath?: string,): string[] { + + projectPath = !projectPath ? this.cwd : projectPath; + + const files = fs.readdirSync(projectPath); + + // uses a recursive function to get all the files in the directory + files.forEach((file) => { + // exclude the node_modules folder + if (!(file === 'node_modules' || file.startsWith('.'))) { + + const filePath = `${projectPath}/${file}`; + const isDirectory = fs.lstatSync(filePath).isDirectory(); + const isSupportedFile = extensions.some((extension) => file.endsWith(extension)); + + if (isDirectory) { + const subFiles = this.getFilesInDirectory(extensions, filePath); + subFiles.length && this.projectFiles.push(...subFiles); + } else if (isSupportedFile) { + this.projectFiles.push(filePath); + } + } + }); + + console.log(`Found ${this.projectFiles.length} files in directory: ${projectPath}`); + + return this.projectFiles; + } + + // gets the content of a file + @Utils.catchError + getFileContent(path: string) { + if (fs.existsSync(path)) { + return { + content: fs.readFileSync(path, 'utf8'), + path, + } + } + throw new Error(`File ${path} does not exist`); + } + + + // writes the content to a file + @Utils.catchError + writeFileContent(path: string, content: string) { + // create file if it does not exist and add content to it + if (!fs.existsSync(path)) { + fs.writeFileSync(path, content, 'utf8'); + } else { + fs.appendFileSync(path, content, 'utf8'); + } + + } + + // deletes a file + @Utils.catchError + delete(path: string) { + if (fs.existsSync(path)) { // check if file exists + fs.unlinkSync(path); // delete file + } + } +} +

ts

+
+
+ +
+

git.ts +

+ /Users/jonathan_atiene/dev/code-critique-ai/src/git.ts +

Criticism

+

+ Issues:
- No major issues detected, code seems to follow best practices and is well-structured.

+

Recommendations

+

Recommendations:
- Consider adding JSDoc comments to functions and methods for better documentation.
- Use destructuring for diffSummary.files.map method in getChangedFiles method for better readability.

+
+ +
+

index.ts +

+ /Users/jonathan_atiene/dev/code-critique-ai/src/index.ts +

Criticism

+

+ Issues:
- Inconsistent naming conventions for private variables (line 4 vs line 5)
- Lack of documentation for class properties and methods
- No error handling for initialization of ConfigStore and FileHandlers (lines 10-11)
- No error handling for reduce method in getScore method (line 22)

+

Recommendations

+

Recommendations:
- Use consistent naming conventions for private variables (either use underscore or not)
- Add JSDoc comments for class properties and methods to improve readability and maintainability
- Implement try-catch blocks for initialization of ConfigStore and FileHandlers to handle errors gracefully
- Add try-catch block for reduce method in getScore method to handle errors gracefully

+

Rewrite

+ Rewrite: + +import { CodeReviewer } from './code-review'; +import { ConfigStore } from './config'; +import { FileHandlers } from './filehandler'; +import { Utils } from './utils'; + +/** + * Auto Reviewer class for code review and scoring + */ +export class AutoReviewer { + private _codeReview: CodeReviewer; + private fileHandler: FileHandlers; + private projectPath: string; + private config: ConfigStore; + + /** + * Constructor for AutoReviewer class + */ + constructor() { + this.projectPath = __dirname; + this.config = new ConfigStore(); + this._codeReview = new CodeReviewer(); + this.fileHandler = new FileHandlers(); + } + + /** + * Method for code review + */ + @Utils.catchError + public async codeReview(): Promise { + Utils.spin('start'); + await this._codeReview.review(); + Utils.spin('stop'); + } + + /** + * Method for getting code review score + * @returns {number} average score + */ + @Utils.catchError + public async getScore(): Promise { + try { + // find the average of the scores + return this._codeReview.scores.reduce((a, b) => a + b, 0) / this._codeReview.scores.length; + } catch (error) { + console.error(error); + return 0; + } + } +} + +
+ +import { CodeReviewer } from './code-review'; +import { ConfigStore } from './config'; +import { FileHandlers } from './filehandler'; +import { Utils } from './utils'; + +export class AutoReviewer { + private _codeReview: CodeReviewer; + private fileHandler: FileHandlers; + projectPath: string; + config: ConfigStore + + constructor() { + this.projectPath = __dirname; + this.config = new ConfigStore(); + this._codeReview = new CodeReviewer(); + this.fileHandler = new FileHandlers(); + } + + @Utils.catchError + public async codeReview() { + Utils.spin('start'); + await this._codeReview.review(); + Utils.spin('stop'); + } + + + @Utils.catchError + public async getScore() { + // find the average of the scores + return this._codeReview.scores.reduce((a, b) => a + b, 0) / this._codeReview.scores.length; + } +} +

ts

+
+
+ +
+

openai.ts +

+ /Users/jonathan_atiene/dev/code-critique-ai/src/openai.ts +

Criticism

+

+ Overall, the code appears to be well-organized and follows good naming conventions. The OpenAI class has a constructor that sets up an OpenAIApi instance and a ConfigStore instance, and there is a method for getting a code review and a method for getting a score based on feedback.

+

Recommendations

+

However, there are a few areas for improvement:

+

Rewrite

+ 1. In the getCodeReview method, the input parameter "rules" is not well-defined. It is unclear what format this parameter should be in or how it should be used. It would be helpful to provide more documentation or examples for this parameter. + +2. The getScore method is not fully implemented - it currently returns a hardcoded value of 1. It would be helpful to add logic for parsing the feedback and returning a score based on that feedback. + +3. The getCompletion method catches and logs errors, but it does not provide any feedback or error handling to the calling function. It would be helpful to either throw an error or return a more informative response when an error occurs. + +Overall, the code appears to be well-written and follows good coding practices. However, there are a few areas for improvement in terms of functionality and error handling. +
+ import { OpenAIApi, Configuration } from 'openai'; +import { ConfigStore } from './config'; +import { Utils } from './utils'; + +export class OpenAI { + openAI: OpenAIApi; + config: ConfigStore; + constructor() { + this.config = new ConfigStore(); + const openAIConfig = new Configuration({ + apiKey: this.config.get('openai_api_key') + }) + this.openAI = new OpenAIApi(openAIConfig); + } + + @Utils.catchError + async getCodeReview(fileContent: string, rules: string) { + if (!rules) { + throw new Error('Rules are required'); + } + const prompt = ` + + review the following code based on the essential rules provided below: + + Rules: + ${rules} + + Git diff: + ${fileContent} + + Review Instructions: + + 1. Identify critical code issues. + 2. Keep your response concise and structured. + 3. Address concerns and provide improvement recommendations. + 4. Do not mention tests except the file path is a test file. + 5. Mention the exact line number of the issue. + 6. Always rewrite the file if there are changes more than 20 lines. + 7. Do not talk about load balancing, scaling, or performance except that's what the code is about. + + Example of a structured review: + + Issues: + - Ambiguous names in calculateTotal (line 12) + - fetchData lacks error handling (line 32) + + Recommendations: + - Rename vars in calculateTotal: a -> price, b -> quantity + - Implement try-catch & error logging in fetchData for robustness + + Rewrite: + + function calculateTotal(a, b) { + return a + b; + } + + + If no rewrite: + - dont add a rewrite if there is none available + + If no issues: + - No issues detected + + `; + + return this.getCompletion(prompt); + + } + + async getScore(feedback: string) { + const prompt = ` + with these review for a file what is your score on a range of 1-10 on code correctness and stability + + reply with only a number + + eg: 1 + + ${feedback} + `; + + // return this.getCompletion(prompt); + return 1; + } + + async getCompletion(prompt: string) { + try { + const response = await this.openAI.createChatCompletion({ + messages: [{ + role: "user", + content: prompt, + }], + model: 'gpt-3.5-turbo-0301', + temperature: 0.5, + }); + + return response.data.choices[0].message?.content; + } catch (e: any) { + console.log(e.message); + return new Error(e.message); + } + } +} +

ts

+
+
+ + + + \ No newline at end of file diff --git a/public/index.js b/public/index.js new file mode 100644 index 0000000..6c535d1 --- /dev/null +++ b/public/index.js @@ -0,0 +1,59 @@ + + + require.config({ + paths: { + 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs' + } + }); + + + let extensionToLanguage = { + 'js': 'javascript', + 'py': 'python', + 'java': 'java', + 'ts': 'typescript', + 'tsx': 'typescript', + 'jsx': 'javascript', + // Add more mappings as needed + }; + + + + require(['vs/editor/editor.main'], function() { + let codeElements = document.querySelectorAll('code'); + let initialCodes = document.querySelectorAll('.original'); + let languages = document.querySelectorAll('.original p'); + + console.log(codeElements.length, initialCodes.length, languages.length); + + codeElements.forEach((codeElement, index) => { + // create a new div element + let div = document.createElement('div'); + div.id = 'editor' + index; + div.className = 'editor'; + + // replace the code element with the div + codeElement.parentNode.replaceChild(div, codeElement); + + // create a Monaco DiffEditor instance in the div + var diffEditor = monaco.editor.createDiffEditor(document.getElementById(div.id), { + theme: 'vs-dark', + }); + + // the original and modified code + const language = languages[index].textContent; + var originalCode = initialCodes[index].textContent; // replace this with the original code + var modifiedCode = codeElement.textContent; + + diffEditor.setModel({ + original: monaco.editor.createModel(originalCode, extensionToLanguage[language]), + modified: monaco.editor.createModel(modifiedCode, extensionToLanguage[language]) + }); + + // After creating the diffEditor, remove the DOM element of the original code + // This will prevent indexing issues by ensuring we're done using the arrays for this iteration before modifying them + initialCodes[index].parentNode.removeChild(initialCodes[index]); + languages[index].parentNode.removeChild(languages[index]); + }); + + }); diff --git a/src/code-review.ts b/src/code-review.ts index b7ad9d7..86d92f7 100644 --- a/src/code-review.ts +++ b/src/code-review.ts @@ -68,9 +68,19 @@ export class CodeReviewer { // gets the code review from the openAI api // get file review in perpendicular order for (const file of queue) { - Utils.sleep(1000) + Utils.sleep(3000) Utils.log('Reviewing:........' + file.path) const codeReview = await this.openAI.getCodeReview(file.content, this.rules); + console.log(`codeReview ${codeReview}`) + if (!codeReview || codeReview instanceof Error) { + Utils.log('No code review found for: ' + file.path) + if (codeReview instanceof Error && codeReview.message.includes('429')) { + Utils.log('You are only allowed to review 3 files per minute on the free version.') + break; + } + return + } + this.printCodeReview({ codeContent: file.content, path: file.path, @@ -91,7 +101,9 @@ export class CodeReviewer { public async printCodeReview(feedback: Feedback): Promise { // prints the code review - this.fileHandler.writeFileContent('feedback.txt', `Path: ${feedback.path} + this.fileHandler.writeFileContent('feedback.txt', + `-------------${'code'} ${'review'}---------------- + Path: ${feedback.path} Criticism: ${feedback.criticism} diff --git a/src/feedback-server.ts b/src/feedback-server.ts new file mode 100644 index 0000000..1ecbc14 --- /dev/null +++ b/src/feedback-server.ts @@ -0,0 +1,175 @@ +import Hapi from '@hapi/hapi'; +import Inert from '@hapi/inert'; +import path from 'path'; +import { FileHandlers } from './filehandler'; + +export class FeedbackServer { + private fileHandler: FileHandlers; + constructor() { + this.fileHandler = new FileHandlers(); + } + async init() { + const server = Hapi.server({ + port: 3123, + host: 'localhost', + routes: { + files: { + relativeTo: path.join(__dirname, '../public') + } + } + }); + + + await this.createFeedBackDocument('feedback.txt'); + + await server.register(Inert); + + server.route({ + method: 'GET', + path: '/{param*}', + handler: { + directory: { + path: '.', + redirectToSlash: true, + index: true, + } + } + }); + + await server.start(); + + console.log('Server running on %s', server.info.uri); + } + + + /** + * this method will create a feedback index.html in the public folder we can move to handle bars in the future + * @param feedbackPath + */ + createFeedBackDocument = async (feedbackPath: string) => { + const { content: fileContent } = await this.fileHandler.getFileContent(path.join(__dirname, '..', feedbackPath)) + + + + let sections = fileContent.split("-------------code review----------------"); + let html = ` + + + + + + + + + + + Code Review + + +
+

Code Reviews

+ ❗️❗️❗️ +

+ Note: This review is AI-generated 🤖. Please critically consider applying only the information that resonates with you 💡. +

+
+ `; + + const allSections = [] + + for (let section of sections) { + + let parts = section.split("\n\n"); + let filePath = parts[0].trim().replace("Path: ", "").trim(); + + /** + * skip the feedback-server.ts file because it contians the splitter for the feedback + * and will cause an error as it will split the file into multiple sections + */ + if (filePath.includes('feedback-server.ts')) { + continue; + } + let criticism = parts[1]; + let recommendations = parts[2]; + let rewrite = parts.slice(3).join("\n\n"); + let originalContent = ""; + + if (criticism === undefined) { + criticism = "No criticism provided for review."; + } + + if (rewrite === undefined) { + rewrite = "No code provided for review."; + } + + + if (rewrite.length > 200 && this.checkReWrite(rewrite).length > 200) { + try { + // get the file content + originalContent = (await this.fileHandler.getFileContent(filePath))?.content + } catch (e) { + continue; + } + const finalOutput = ` +
+

${path.basename(filePath)} +

+ ${filePath} +

Criticism

+

${criticism?.replace("Criticism: ", "").replace(/\n- /g, "
- ")}

+

Recommendations

+

${recommendations?.replace("Recommendations: ", "").replace(/\n- /g, "
- ")}

+

Rewrite

+ ${this.checkReWrite(rewrite)} +
+ ${originalContent} +

${path.extname(filePath).replace(".", "").trim()}

+
+
+ `; + allSections.push(finalOutput); + html += finalOutput + } else { + const finalOutput = ` +
+

${path.basename(filePath)} +

+ ${filePath} +

Criticism

+

${criticism?.replace("Criticism: ", "").replace(/\n- /g, "
- ")}

+

Recommendations

+

${recommendations?.replace("Recommendations: ", "").replace(/\n- /g, "
- ")}

+
+ `; + html += finalOutput + allSections.push(finalOutput); + } + } + + html += ` + + + `; + + this.fileHandler.delete(path.join(__dirname, '../public/index.html')); + this.fileHandler.writeFileContent(path.join(__dirname, '../public/index.html'), html); + + } + + + checkReWrite = (rewrite: string) => { + rewrite = rewrite.trim(); + if (rewrite.length > 200) { + if (rewrite.includes("```")) { + rewrite = rewrite.replace("```", ""); + rewrite = rewrite.replace("```", ""); + return ` ${rewrite} ` + } + return rewrite; + } + return ""; + + } + + +} \ No newline at end of file diff --git a/src/openai.ts b/src/openai.ts index bae66e3..71685ed 100644 --- a/src/openai.ts +++ b/src/openai.ts @@ -13,7 +13,6 @@ export class OpenAI { this.openAI = new OpenAIApi(openAIConfig); } - @Utils.catchError async getCodeReview(fileContent: string, rules: string) { if (!rules) { @@ -34,6 +33,10 @@ export class OpenAI { 1. Identify critical code issues. 2. Keep your response concise and structured. 3. Address concerns and provide improvement recommendations. + 4. Do not mention tests except the file path is a test file. + 5. Mention the exact line number of the issue. + 6. Always rewrite the file if there are changes more than 20 lines. + 7. Do not talk about load balancing, scaling, or performance except that's what the code is about. Example of a structured review: @@ -44,6 +47,16 @@ export class OpenAI { Recommendations: - Rename vars in calculateTotal: a -> price, b -> quantity - Implement try-catch & error logging in fetchData for robustness + + Rewrite: + + function calculateTotal(a, b) { + return a + b; + } + + + If no rewrite: + - dont add a rewrite if there is none available If no issues: - No issues detected @@ -65,19 +78,25 @@ export class OpenAI { ${feedback} `; - return this.getCompletion(prompt); + // return this.getCompletion(prompt); + return 1; } async getCompletion(prompt: string) { - const response = await this.openAI.createChatCompletion({ - messages: [{ - role: "user", - content: prompt, - }], - model: 'gpt-3.5-turbo', - }); - + try { + const response = await this.openAI.createChatCompletion({ + messages: [{ + role: "user", + content: prompt, + }], + model: 'gpt-3.5-turbo-0301', + temperature: 0.5, + }); - return response.data.choices[0].message?.content; + return response.data.choices[0].message?.content; + } catch (e: any) { + console.log(e.message); + return new Error(e.message); + } } } \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 32f5378..b0a1f01 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -30,7 +30,7 @@ export class Utils { static spin = (status: "start" | "stop") => { - let interval; + let interval; const spinner = ['|', '/', '-', '\\']; @@ -46,7 +46,7 @@ export class Utils { return true; } - + } }