codereviewerai 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,127 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import boxen from 'boxen';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ // FIX: Named import for simpleGit is required in ESM/TS
7
+ import { simpleGit, SimpleGit } from 'simple-git';
8
+ import { ConfigManager } from '../config/manager.js';
9
+ import { AIProvider } from '../ai/provider.js';
10
+ import { ContextManager } from './context.js';
11
+
12
+ export class ReviewEngine {
13
+ private configManager: ConfigManager;
14
+ private aiProvider: AIProvider;
15
+ private contextManager: ContextManager;
16
+ private git: SimpleGit;
17
+
18
+ constructor(configManager: ConfigManager) {
19
+ this.configManager = configManager;
20
+ this.aiProvider = new AIProvider(configManager);
21
+ this.contextManager = new ContextManager();
22
+ // FIX: Standard initialization call
23
+ this.git = simpleGit();
24
+ }
25
+
26
+ /**
27
+ * Reviews a specific file by reading its current content
28
+ */
29
+ async reviewFile(filePath: string): Promise<void> {
30
+ const spinner = ora(`Reading ${path.basename(filePath)}...`).start();
31
+
32
+ try {
33
+ const fullPath = path.resolve(process.cwd(), filePath);
34
+ if (!fs.existsSync(fullPath)) {
35
+ spinner.fail(chalk.red(`File not found: ${filePath}`));
36
+ return;
37
+ }
38
+
39
+ const code = fs.readFileSync(fullPath, 'utf-8');
40
+
41
+ // Optimization: Get the actual changes to reduce AI token usage
42
+ const diff = await this.git.diff([filePath]);
43
+
44
+ spinner.text = 'AI is analyzing your changes...';
45
+
46
+ const context = await this.contextManager.getContext(filePath);
47
+
48
+ // Logic: If there is a diff, review the diff. Otherwise, review the whole file.
49
+ const review = await this.aiProvider.reviewCode(diff || code, filePath, context);
50
+
51
+ spinner.succeed(chalk.green('Review complete!'));
52
+ this.displayReview(filePath, review);
53
+
54
+ await this.contextManager.addToContext(filePath, code, review);
55
+
56
+ } catch (error: any) {
57
+ spinner.fail(chalk.red('Review failed: ' + error.message));
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Reviews only files that are currently in the Git Staging Area
63
+ */
64
+ async reviewStaged(): Promise<void> {
65
+ const spinner = ora('Checking staged files...').start();
66
+
67
+ try {
68
+ const status = await this.git.status();
69
+ const stagedFiles = status.staged;
70
+
71
+ if (stagedFiles.length === 0) {
72
+ spinner.info(chalk.yellow('No staged files found. Use "git add" first.'));
73
+ return;
74
+ }
75
+
76
+ spinner.succeed(chalk.green(`Found ${stagedFiles.length} staged file(s)`));
77
+
78
+ for (const file of stagedFiles) {
79
+ console.log(chalk.cyan(`\nšŸ“„ File: ${file}`));
80
+ // Ensure file is a string before passing
81
+ await this.reviewFile(file as string);
82
+ }
83
+ } catch (error: any) {
84
+ spinner.fail(chalk.red('Git Error: ' + error.message));
85
+ }
86
+ }
87
+
88
+ private displayReview(filePath: string, review: any): void {
89
+ // Safe check for the score to avoid crashes if the AI returns a string
90
+ const score = parseInt(review.score) || 0;
91
+ const scoreColor = score >= 8 ? chalk.green : score >= 5 ? chalk.yellow : chalk.red;
92
+
93
+ let content = `${chalk.bold('Summary:')} ${review.summary}\n`;
94
+ content += `${chalk.bold('Score:')} ${scoreColor(score + '/10')}\n\n`;
95
+
96
+ if (review.issues && review.issues.length > 0) {
97
+ content += `${chalk.red.bold('šŸš€ Issues Found:')}\n`;
98
+ review.issues.forEach((issue: any) => {
99
+ content += `${chalk.yellow('•')} [Line ${issue.line || '?'}] ${issue.msg}\n`;
100
+ if (issue.fix) content += ` ${chalk.gray('Suggested Fix:')} ${chalk.italic(issue.fix)}\n`;
101
+ });
102
+ } else {
103
+ content += chalk.green('āœ… No critical issues found. Great job!\n');
104
+ }
105
+
106
+ if (review.optimizations && Array.isArray(review.optimizations)) {
107
+ content += `\n${chalk.blue.bold('⚔ Optimizations:')}\n`;
108
+ review.optimizations.forEach((opt: string) => {
109
+ content += `${chalk.blue('→')} ${opt}\n`;
110
+ });
111
+ }
112
+
113
+ console.log(boxen(content, {
114
+ title: filePath,
115
+ titleAlignment: 'center',
116
+ padding: 1,
117
+ margin: 1,
118
+ borderStyle: 'round',
119
+ borderColor: 'cyan'
120
+ }));
121
+ }
122
+
123
+ async clearHistory(): Promise<void> {
124
+ await this.contextManager.clearHistory();
125
+ console.log(chalk.green('āœ… Local history cleared.'));
126
+ }
127
+ }
@@ -0,0 +1,86 @@
1
+ import { watch, FSWatcher } from 'chokidar'; // Fixed import
2
+ import chalk from 'chalk';
3
+ import path from 'path';
4
+ import { ConfigManager } from '../config/manager.js';
5
+ import { ReviewEngine } from './reviewer.js';
6
+
7
+ export class FileWatcher {
8
+ // Fixed: Using the explicitly imported FSWatcher type
9
+ private watcher: FSWatcher | null = null;
10
+ private reviewer: ReviewEngine;
11
+ private configManager: ConfigManager;
12
+
13
+ constructor(configManager: ConfigManager) {
14
+ this.configManager = configManager;
15
+ this.reviewer = new ReviewEngine(configManager);
16
+ }
17
+
18
+ /**
19
+ * Starts the background process to watch for file changes
20
+ */
21
+ async start(): Promise<void> {
22
+ // Clear terminal for a clean "Watch Mode" UI
23
+ console.clear();
24
+ console.log(chalk.bold.green('šŸ‘€ codereviewer.ai is now watching your code...'));
25
+ console.log(chalk.gray('Press Ctrl+C to stop the watcher.\n'));
26
+
27
+ // Fixed: Use the watch() function directly
28
+ this.watcher = watch(process.cwd(), {
29
+ ignored: [
30
+ /(^|[\/\\])\../, // ignore dotfiles (.git, .awesomediagns)
31
+ '**/node_modules/**',
32
+ '**/dist/**',
33
+ '**/package-lock.json'
34
+ ],
35
+ persistent: true,
36
+ ignoreInitial: true,
37
+ awaitWriteFinish: {
38
+ stabilityThreshold: 500,
39
+ pollInterval: 100
40
+ }
41
+ });
42
+
43
+ // Event listener for saved changes
44
+ this.watcher.on('change', async (filePath: string) => {
45
+ const fileName = path.basename(filePath);
46
+ const ext = path.extname(filePath);
47
+
48
+ const supportedExtensions = ['.ts', '.js', '.py', '.go', '.cpp', '.java', '.tsx', '.jsx'];
49
+
50
+ if (supportedExtensions.includes(ext)) {
51
+ console.log(chalk.cyan(`\nšŸ’¾ Change detected in: ${fileName}`));
52
+
53
+ // Trigger the review engine
54
+ try {
55
+ await this.reviewer.reviewFile(filePath);
56
+ } catch (error) {
57
+ console.error(chalk.red('Review failed during auto-watch.'));
58
+ }
59
+
60
+ console.log(chalk.gray('\nWaiting for next change...'));
61
+ }
62
+ });
63
+
64
+ this.watcher.on('error', (error: unknown) => {
65
+ console.error(chalk.red(`Watcher error: ${(error as Error).message}`));
66
+ });
67
+
68
+ // Handle process termination to clean up the watcher
69
+ process.on('SIGINT', () => {
70
+ this.stop();
71
+ // Give the close() promise a moment to resolve before exiting
72
+ setTimeout(() => process.exit(0), 100);
73
+ });
74
+ }
75
+
76
+ /**
77
+ * Stops the watcher safely
78
+ */
79
+ stop(): void {
80
+ if (this.watcher) {
81
+ this.watcher.close().then(() => {
82
+ console.log(chalk.yellow('\nšŸ›‘ Watcher stopped. Goodbye!'));
83
+ });
84
+ }
85
+ }
86
+ }
package/src/index.ts ADDED
@@ -0,0 +1,109 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { homePage } from './ui/home.js'; // Note the .js extension for ESM
4
+ import { ConfigWizard } from './config/wizard.js';
5
+ import { ConfigManager } from './config/manager.js';
6
+ import { ReviewEngine } from './core/reviewer.js';
7
+ import { FileWatcher } from './core/watcher.js';
8
+ import { ChatInterface } from './ui/chat.js';
9
+ import fs from 'fs';
10
+ import path from 'path';
11
+ import { fileURLToPath } from 'url';
12
+
13
+ // Fix for __dirname in ESM
14
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
+ const packageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf8'));
16
+
17
+ const program = new Command();
18
+ const configManager = new ConfigManager();
19
+ const configWizard = new ConfigWizard();
20
+
21
+ program
22
+ .name('awd')
23
+ .description('codereviewer.ai - AI-powered code review for developers')
24
+ .version(packageJson.version)
25
+ .alias('awesomediagns');
26
+
27
+ // --- 1. Home / Help ---
28
+ program
29
+ .command('home')
30
+ .description('Display the AwesomeDiagns home screen')
31
+ .action(async () => {
32
+ await homePage.displayWelcome();
33
+ homePage.displayQuickHelp();
34
+ });
35
+
36
+ // --- 2. Configuration ---
37
+ program
38
+ .command('init')
39
+ .description('Initialize your AI provider and API keys')
40
+ .action(async () => {
41
+ await configWizard.runSetup();
42
+ });
43
+
44
+ program
45
+ .command('config')
46
+ .description('View or update current configuration')
47
+ .option('-s, --show', 'Show current config')
48
+ .option('-r, --reset', 'Clear all settings')
49
+ .action(async (options) => {
50
+ if (options.show) {
51
+ configManager.displayConfig();
52
+ } else if (options.reset) {
53
+ configManager.clearConfig();
54
+ } else {
55
+ await configWizard.runSetup();
56
+ }
57
+ });
58
+
59
+ // --- 3. Code Review ---
60
+ program
61
+ .command('review [file]')
62
+ .description('Review staged changes or a specific file')
63
+ .option('-s, --staged', 'Review only files in git staging area', true)
64
+ .action(async (file, options) => {
65
+ if (!configManager.isConfigured()) {
66
+ console.log(chalk.red('\nāŒ Not configured! Run: ') + chalk.white('awd init'));
67
+ return;
68
+ }
69
+ const reviewer = new ReviewEngine(configManager);
70
+ if (file) {
71
+ await reviewer.reviewFile(file);
72
+ } else {
73
+ await reviewer.reviewStaged();
74
+ }
75
+ });
76
+
77
+ // --- 4. Auto-Mode (Watcher) ---
78
+ program
79
+ .command('watch')
80
+ .description('Start real-time auto-review mode (on save)')
81
+ .action(async () => {
82
+ if (!configManager.isConfigured()) return;
83
+ const watcher = new FileWatcher(configManager);
84
+ await watcher.start();
85
+ });
86
+
87
+ // --- 5. Interactive Chat ---
88
+ program
89
+ .command('chat')
90
+ .description('Start a conversation with the AI about your code')
91
+ .action(async () => {
92
+ const chat = new ChatInterface(configManager);
93
+ await chat.start();
94
+ });
95
+
96
+ // --- Default Action ---
97
+ // If the user just types 'awd', show the home page
98
+ program.action(async () => {
99
+ await homePage.displayWelcome();
100
+ homePage.displayQuickHelp();
101
+ });
102
+
103
+ // Handle unknown commands
104
+ program.on('command:*', () => {
105
+ console.error(chalk.red('\nInvalid command: %s\nSee --help for a list of available commands.'), program.args.join(' '));
106
+ process.exit(1);
107
+ });
108
+
109
+ program.parse(process.argv);
package/src/ui/chat.ts ADDED
@@ -0,0 +1,111 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { ConfigManager } from '../config/manager.js';
5
+ import { AIProvider } from '../ai/provider.js';
6
+ import { ContextManager } from '../core/context.js';
7
+ // Note: We import ChatMessage as a type if your TS version requires it
8
+ import type { ChatMessage } from '../core/context.js';
9
+
10
+ export class ChatInterface {
11
+ private configManager: ConfigManager;
12
+ private aiProvider: AIProvider;
13
+ private contextManager: ContextManager;
14
+ private conversationHistory: ChatMessage[] = [];
15
+
16
+ constructor(configManager: ConfigManager) {
17
+ this.configManager = configManager;
18
+ this.aiProvider = new AIProvider(configManager);
19
+ this.contextManager = new ContextManager();
20
+ }
21
+
22
+ async start(): Promise<void> {
23
+ console.clear();
24
+ console.log(chalk.cyan.bold('╔════════════════════════════════════════════╗'));
25
+ console.log(chalk.cyan.bold('ā•‘') + chalk.bold.white(' šŸ’¬ codereviewer.ai Chat Mode ') + chalk.cyan.bold('ā•‘'));
26
+ console.log(chalk.cyan.bold('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•\n'));
27
+
28
+ console.log(chalk.gray('Type your questions about the code or reviews.'));
29
+ console.log(chalk.gray('Commands: "exit" to quit, "clear" to reset history.\n'));
30
+
31
+ // Load existing history
32
+ this.conversationHistory = await this.contextManager.getChatHistory();
33
+
34
+ if (this.conversationHistory.length > 0) {
35
+ console.log(chalk.yellow(`šŸ“– Loaded ${this.conversationHistory.length} previous messages.\n`));
36
+ this.displayLastExchange();
37
+ }
38
+
39
+ await this.chatLoop();
40
+ }
41
+
42
+ private async chatLoop(): Promise<void> {
43
+ while (true) {
44
+ // Fix: Inquirer v9+ requires this specific handling for async loops
45
+ const answers = await inquirer.prompt([
46
+ {
47
+ type: 'input',
48
+ name: 'userInput',
49
+ message: chalk.green('You:'),
50
+ validate: (input: string) => input.trim().length > 0 || 'Please enter a message.'
51
+ }
52
+ ]);
53
+
54
+ const userInput = answers.userInput;
55
+ const command = userInput.trim().toLowerCase();
56
+
57
+ if (command === 'exit' || command === 'quit') {
58
+ await this.contextManager.saveChatHistory(this.conversationHistory);
59
+ console.log(chalk.yellow('\nšŸ‘‹ Chat session saved. Goodbye!'));
60
+ break;
61
+ }
62
+
63
+ if (command === 'clear') {
64
+ this.conversationHistory = [];
65
+ await this.contextManager.saveChatHistory([]);
66
+ console.log(chalk.red('šŸ—‘ļø Chat history cleared.\n'));
67
+ continue;
68
+ }
69
+
70
+ await this.handleResponse(userInput);
71
+ }
72
+ }
73
+
74
+ private async handleResponse(message: string): Promise<void> {
75
+ const spinner = ora('AI is thinking...').start();
76
+
77
+ try {
78
+ this.conversationHistory.push({ role: 'user', content: message });
79
+
80
+ // Ensure your AIProvider.ts actually has the .chat() method!
81
+ const response = await this.aiProvider.chat(message, this.conversationHistory);
82
+
83
+ spinner.stop();
84
+
85
+ console.log(`\n${chalk.cyan.bold('AI:')} ${chalk.white(response)}\n`);
86
+
87
+ this.conversationHistory.push({ role: 'assistant', content: response });
88
+
89
+ // Keep context window manageable (e.g., last 20 messages)
90
+ if (this.conversationHistory.length > 20) {
91
+ this.conversationHistory = this.conversationHistory.slice(-20);
92
+ }
93
+
94
+ await this.contextManager.saveChatHistory(this.conversationHistory);
95
+
96
+ } catch (error: any) {
97
+ spinner.fail(chalk.red('Error: ' + (error.message || 'Unknown AI error')));
98
+ }
99
+ }
100
+
101
+ private displayLastExchange(): void {
102
+ const lastTwo = this.conversationHistory.slice(-2);
103
+ lastTwo.forEach(msg => {
104
+ const label = msg.role === 'user' ? chalk.green('You:') : chalk.cyan.bold('AI:');
105
+ // Clean display of previous chat content
106
+ const preview = msg.content.length > 100 ? msg.content.substring(0, 100) + '...' : msg.content;
107
+ console.log(`${label} ${chalk.gray(preview)}`);
108
+ });
109
+ console.log(chalk.gray('————————————————————————————————─────────────\n'));
110
+ }
111
+ }
package/src/ui/home.ts ADDED
@@ -0,0 +1,60 @@
1
+ import chalk from 'chalk';
2
+ import figlet from 'figlet';
3
+ import gradient from 'gradient-string';
4
+
5
+ export const homePage = {
6
+ /**
7
+ * Displays the high-tech matrix-style header
8
+ */
9
+ displayWelcome: async () => {
10
+ // Ensure the terminal is clear for the professional 'splash' effect
11
+ console.clear();
12
+
13
+ // Fix: Store the color in a constant for cleaner reuse
14
+ const matrixGreen = chalk.hex('#00FF41');
15
+
16
+ // Your specific ASCII Art - Wrapped in a raw string to protect backslashes
17
+ console.log(matrixGreen.bold(`
18
+ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—
19
+ ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—
20
+ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘ ā–ˆā•— ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•
21
+ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā• ā•šā–ˆā–ˆā•— ā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—
22
+ ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•šā–ˆā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā•šā–ˆā–ˆā–ˆā•”ā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘
23
+ ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā•ā•šā•ā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā• ā•šā•ā•ā•šā•ā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•šā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā•.ai
24
+ `));
25
+
26
+ // Adding a subtle subtitle using gradient
27
+ try {
28
+ const subHeader = figlet.textSync('CODEREVIEWER', {
29
+ font: 'Small',
30
+ horizontalLayout: 'default',
31
+ verticalLayout: 'default',
32
+ });
33
+ console.log(gradient(['#00FF41', '#008F11']).multiline(subHeader));
34
+ } catch (err) {
35
+ // Fallback if figlet fails
36
+ console.log(matrixGreen.bold(' --- CODEREVIEWER.AI --- '));
37
+ }
38
+
39
+ console.log(chalk.gray('————————————————————————————————————————————————————————————————————'));
40
+ console.log(chalk.white(' Advanced AI Analysis • Performance Optimization • Security Audit'));
41
+ console.log(chalk.gray('————————————————————————————————————————————————————————————————————\n'));
42
+ },
43
+
44
+ /**
45
+ * Shows a clean list of available commands
46
+ */
47
+ displayQuickHelp: () => {
48
+ console.log(chalk.bold.white('šŸ“‚ GETTING STARTED'));
49
+ console.log(` ${chalk.green('awd init')} ${chalk.gray('→ Setup your API keys and provider')}`);
50
+ console.log(` ${chalk.green('awd review')} ${chalk.gray('→ Review staged changes in Git')}`);
51
+ console.log(` ${chalk.green('awd watch')} ${chalk.gray('→ Enable real-time auto-review mode')}`);
52
+ console.log(` ${chalk.green('awd chat')} ${chalk.gray('→ Discuss results with the AI assistant')}\n`);
53
+
54
+ console.log(chalk.bold.white('āš™ļø SYSTEM'));
55
+ console.log(` ${chalk.green('awd status')} ${chalk.gray('→ Show current configuration')}`);
56
+ console.log(` ${chalk.green('awd history')} ${chalk.gray('→ View previous review scores')}\n`);
57
+
58
+ console.log(chalk.hex('#00FF41')('Ready for input...'));
59
+ }
60
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "sourceMap": true,
14
+ "declaration": false
15
+ },
16
+ "include": ["src/**/*"],
17
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
18
+ }