vigthoria-cli 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.
- package/README.md +413 -0
- package/dist/commands/auth.d.ts +24 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +194 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/chat.d.ts +64 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +596 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/config.d.ts +25 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +291 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/edit.d.ts +28 -0
- package/dist/commands/edit.d.ts.map +1 -0
- package/dist/commands/edit.js +257 -0
- package/dist/commands/edit.js.map +1 -0
- package/dist/commands/explain.d.ts +21 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +98 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/generate.d.ts +25 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +155 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/review.d.ts +24 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +153 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +205 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/api.d.ts +88 -0
- package/dist/utils/api.d.ts.map +1 -0
- package/dist/utils/api.js +431 -0
- package/dist/utils/api.js.map +1 -0
- package/dist/utils/config.d.ts +57 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +167 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/files.d.ts +31 -0
- package/dist/utils/files.d.ts.map +1 -0
- package/dist/utils/files.js +217 -0
- package/dist/utils/files.js.map +1 -0
- package/dist/utils/logger.d.ts +23 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +104 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/session.d.ts +61 -0
- package/dist/utils/session.d.ts.map +1 -0
- package/dist/utils/session.js +172 -0
- package/dist/utils/session.js.map +1 -0
- package/dist/utils/tools.d.ts +145 -0
- package/dist/utils/tools.d.ts.map +1 -0
- package/dist/utils/tools.js +781 -0
- package/dist/utils/tools.js.map +1 -0
- package/install.sh +248 -0
- package/package.json +52 -0
- package/src/commands/auth.ts +225 -0
- package/src/commands/chat.ts +690 -0
- package/src/commands/config.ts +297 -0
- package/src/commands/edit.ts +310 -0
- package/src/commands/explain.ts +115 -0
- package/src/commands/generate.ts +177 -0
- package/src/commands/review.ts +186 -0
- package/src/index.ts +221 -0
- package/src/types/marked-terminal.d.ts +31 -0
- package/src/utils/api.ts +531 -0
- package/src/utils/config.ts +224 -0
- package/src/utils/files.ts +212 -0
- package/src/utils/logger.ts +125 -0
- package/src/utils/session.ts +167 -0
- package/src/utils/tools.ts +933 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Explain Command - Explain code in files
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import { Marked } from 'marked';
|
|
8
|
+
import { markedTerminal } from 'marked-terminal';
|
|
9
|
+
import { Config } from '../utils/config.js';
|
|
10
|
+
import { Logger } from '../utils/logger.js';
|
|
11
|
+
import { APIClient } from '../utils/api.js';
|
|
12
|
+
import { FileUtils } from '../utils/files.js';
|
|
13
|
+
|
|
14
|
+
interface ExplainOptions {
|
|
15
|
+
lines?: string;
|
|
16
|
+
detail: 'brief' | 'normal' | 'detailed';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class ExplainCommand {
|
|
20
|
+
private config: Config;
|
|
21
|
+
private logger: Logger;
|
|
22
|
+
private api: APIClient;
|
|
23
|
+
private fileUtils: FileUtils;
|
|
24
|
+
private marked: Marked;
|
|
25
|
+
|
|
26
|
+
constructor(config: Config, logger: Logger) {
|
|
27
|
+
this.config = config;
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
this.api = new APIClient(config, logger);
|
|
30
|
+
this.fileUtils = new FileUtils(process.cwd(), config.get('project').ignorePatterns);
|
|
31
|
+
|
|
32
|
+
this.marked = new Marked();
|
|
33
|
+
this.marked.use(markedTerminal() as any);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async run(filePath: string, options: ExplainOptions): Promise<void> {
|
|
37
|
+
// Check auth
|
|
38
|
+
if (!this.config.isAuthenticated()) {
|
|
39
|
+
this.logger.error('Not authenticated. Run: vigthoria login');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Read file
|
|
44
|
+
const file = this.fileUtils.readFile(filePath);
|
|
45
|
+
if (!file) {
|
|
46
|
+
this.logger.error(`File not found: ${filePath}`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Get code to explain
|
|
51
|
+
let codeToExplain = file.content;
|
|
52
|
+
let lineInfo = '';
|
|
53
|
+
|
|
54
|
+
if (options.lines) {
|
|
55
|
+
const [start, end] = options.lines.split('-').map(Number);
|
|
56
|
+
const lines = this.fileUtils.readLines(filePath, start, end || start);
|
|
57
|
+
if (lines) {
|
|
58
|
+
codeToExplain = lines;
|
|
59
|
+
lineInfo = ` (lines ${start}-${end || start})`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.logger.section(`Explaining: ${file.relativePath}${lineInfo}`);
|
|
64
|
+
console.log(chalk.gray(`Language: ${file.language} | Detail: ${options.detail}`));
|
|
65
|
+
console.log();
|
|
66
|
+
|
|
67
|
+
// Show the code being explained
|
|
68
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
69
|
+
codeToExplain.split('\n').forEach((line, i) => {
|
|
70
|
+
const lineNum = chalk.gray(String(i + 1).padStart(4, ' ') + ' │ ');
|
|
71
|
+
console.log(lineNum + line);
|
|
72
|
+
});
|
|
73
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
74
|
+
console.log();
|
|
75
|
+
|
|
76
|
+
const spinner = ora({
|
|
77
|
+
text: 'Analyzing code...',
|
|
78
|
+
spinner: 'dots',
|
|
79
|
+
}).start();
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const explanation = await this.api.explainCode(codeToExplain, file.language);
|
|
83
|
+
|
|
84
|
+
spinner.stop();
|
|
85
|
+
|
|
86
|
+
// Format based on detail level
|
|
87
|
+
const formattedExplanation = this.formatExplanation(explanation, options.detail);
|
|
88
|
+
|
|
89
|
+
this.logger.section('Explanation');
|
|
90
|
+
console.log(this.marked.parse(formattedExplanation));
|
|
91
|
+
|
|
92
|
+
} catch (error) {
|
|
93
|
+
spinner.stop();
|
|
94
|
+
this.logger.error('Explanation failed:', (error as Error).message);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private formatExplanation(explanation: string, detail: string): string {
|
|
99
|
+
switch (detail) {
|
|
100
|
+
case 'brief':
|
|
101
|
+
// Extract just the summary
|
|
102
|
+
const lines = explanation.split('\n');
|
|
103
|
+
const summaryLines = lines.slice(0, Math.min(lines.length, 5));
|
|
104
|
+
return summaryLines.join('\n');
|
|
105
|
+
|
|
106
|
+
case 'detailed':
|
|
107
|
+
// Add extra formatting
|
|
108
|
+
return explanation + '\n\n---\n*Detailed analysis by Vigthoria*';
|
|
109
|
+
|
|
110
|
+
case 'normal':
|
|
111
|
+
default:
|
|
112
|
+
return explanation;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate Command - Generate code from description
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
|
+
import { Config } from '../utils/config.js';
|
|
9
|
+
import { Logger } from '../utils/logger.js';
|
|
10
|
+
import { APIClient } from '../utils/api.js';
|
|
11
|
+
import { FileUtils } from '../utils/files.js';
|
|
12
|
+
|
|
13
|
+
interface GenerateOptions {
|
|
14
|
+
language: string;
|
|
15
|
+
output?: string;
|
|
16
|
+
model: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class GenerateCommand {
|
|
20
|
+
private config: Config;
|
|
21
|
+
private logger: Logger;
|
|
22
|
+
private api: APIClient;
|
|
23
|
+
private fileUtils: FileUtils;
|
|
24
|
+
|
|
25
|
+
constructor(config: Config, logger: Logger) {
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.logger = logger;
|
|
28
|
+
this.api = new APIClient(config, logger);
|
|
29
|
+
this.fileUtils = new FileUtils(process.cwd(), config.get('project').ignorePatterns);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async run(description: string, options: GenerateOptions): Promise<void> {
|
|
33
|
+
// Check auth
|
|
34
|
+
if (!this.config.isAuthenticated()) {
|
|
35
|
+
this.logger.error('Not authenticated. Run: vigthoria login');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.logger.section('Code Generation');
|
|
40
|
+
console.log(chalk.gray(`Language: ${options.language}`));
|
|
41
|
+
console.log(chalk.gray(`Description: ${description}`));
|
|
42
|
+
console.log();
|
|
43
|
+
|
|
44
|
+
const spinner = ora({
|
|
45
|
+
text: 'Generating code...',
|
|
46
|
+
spinner: 'dots',
|
|
47
|
+
}).start();
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const code = await this.api.generateCode(description, options.language, options.model);
|
|
51
|
+
|
|
52
|
+
spinner.stop();
|
|
53
|
+
|
|
54
|
+
// Display generated code
|
|
55
|
+
this.logger.section('Generated Code');
|
|
56
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
57
|
+
console.log(this.highlightCode(code, options.language));
|
|
58
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
59
|
+
console.log();
|
|
60
|
+
|
|
61
|
+
// Save options
|
|
62
|
+
if (options.output) {
|
|
63
|
+
await this.saveToFile(options.output, code);
|
|
64
|
+
} else {
|
|
65
|
+
await this.promptForAction(code, options.language);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
} catch (error) {
|
|
69
|
+
spinner.stop();
|
|
70
|
+
this.logger.error('Generation failed:', (error as Error).message);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private highlightCode(code: string, language: string): string {
|
|
75
|
+
// Basic syntax highlighting
|
|
76
|
+
// In production, use a proper library like highlight.js
|
|
77
|
+
const lines = code.split('\n');
|
|
78
|
+
|
|
79
|
+
return lines.map((line, i) => {
|
|
80
|
+
const lineNum = chalk.gray(String(i + 1).padStart(4, ' ') + ' │ ');
|
|
81
|
+
return lineNum + this.highlightLine(line, language);
|
|
82
|
+
}).join('\n');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private highlightLine(line: string, language: string): string {
|
|
86
|
+
// Simple keyword highlighting
|
|
87
|
+
const keywords: Record<string, string[]> = {
|
|
88
|
+
typescript: ['const', 'let', 'var', 'function', 'class', 'interface', 'type', 'export', 'import', 'from', 'async', 'await', 'return', 'if', 'else', 'for', 'while', 'try', 'catch', 'throw', 'new'],
|
|
89
|
+
javascript: ['const', 'let', 'var', 'function', 'class', 'export', 'import', 'from', 'async', 'await', 'return', 'if', 'else', 'for', 'while', 'try', 'catch', 'throw', 'new'],
|
|
90
|
+
python: ['def', 'class', 'import', 'from', 'return', 'if', 'elif', 'else', 'for', 'while', 'try', 'except', 'raise', 'with', 'as', 'async', 'await', 'yield', 'lambda'],
|
|
91
|
+
rust: ['fn', 'let', 'mut', 'const', 'struct', 'enum', 'impl', 'trait', 'pub', 'use', 'mod', 'return', 'if', 'else', 'match', 'for', 'while', 'loop', 'async', 'await'],
|
|
92
|
+
go: ['func', 'var', 'const', 'type', 'struct', 'interface', 'package', 'import', 'return', 'if', 'else', 'for', 'range', 'switch', 'case', 'go', 'defer', 'chan'],
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const langKeywords = keywords[language] || keywords['typescript'];
|
|
96
|
+
let highlighted = line;
|
|
97
|
+
|
|
98
|
+
// Highlight keywords
|
|
99
|
+
langKeywords.forEach(kw => {
|
|
100
|
+
const regex = new RegExp(`\\b${kw}\\b`, 'g');
|
|
101
|
+
highlighted = highlighted.replace(regex, chalk.magenta(kw));
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Highlight strings
|
|
105
|
+
highlighted = highlighted.replace(/(["'`])(?:(?!\1)[^\\]|\\.)*\1/g, (match) => chalk.green(match));
|
|
106
|
+
|
|
107
|
+
// Highlight comments
|
|
108
|
+
highlighted = highlighted.replace(/(\/\/.*$|#.*$)/g, (match) => chalk.gray(match));
|
|
109
|
+
|
|
110
|
+
// Highlight numbers
|
|
111
|
+
highlighted = highlighted.replace(/\b(\d+)\b/g, (match) => chalk.yellow(match));
|
|
112
|
+
|
|
113
|
+
return highlighted;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async saveToFile(filePath: string, code: string): Promise<void> {
|
|
117
|
+
if (this.fileUtils.writeFile(filePath, code)) {
|
|
118
|
+
this.logger.success(`Code saved to ${filePath}`);
|
|
119
|
+
} else {
|
|
120
|
+
this.logger.error('Failed to save file');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private async promptForAction(code: string, language: string): Promise<void> {
|
|
125
|
+
const { action } = await inquirer.prompt([
|
|
126
|
+
{
|
|
127
|
+
type: 'list',
|
|
128
|
+
name: 'action',
|
|
129
|
+
message: 'What would you like to do with this code?',
|
|
130
|
+
choices: [
|
|
131
|
+
{ name: 'Copy to clipboard', value: 'copy' },
|
|
132
|
+
{ name: 'Save to file', value: 'save' },
|
|
133
|
+
{ name: 'Regenerate', value: 'regenerate' },
|
|
134
|
+
{ name: 'Done', value: 'done' },
|
|
135
|
+
],
|
|
136
|
+
},
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
switch (action) {
|
|
140
|
+
case 'copy':
|
|
141
|
+
// Note: Clipboard access requires additional setup
|
|
142
|
+
this.logger.info('Code copied to clipboard');
|
|
143
|
+
console.log(chalk.gray('(Note: Clipboard access may require additional permissions)'));
|
|
144
|
+
break;
|
|
145
|
+
case 'save':
|
|
146
|
+
const { filename } = await inquirer.prompt([
|
|
147
|
+
{
|
|
148
|
+
type: 'input',
|
|
149
|
+
name: 'filename',
|
|
150
|
+
message: 'Enter filename:',
|
|
151
|
+
default: this.suggestFilename(language),
|
|
152
|
+
},
|
|
153
|
+
]);
|
|
154
|
+
await this.saveToFile(filename, code);
|
|
155
|
+
break;
|
|
156
|
+
case 'regenerate':
|
|
157
|
+
this.logger.info('Use the generate command again with different parameters');
|
|
158
|
+
break;
|
|
159
|
+
case 'done':
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private suggestFilename(language: string): string {
|
|
165
|
+
const extensions: Record<string, string> = {
|
|
166
|
+
typescript: 'generated.ts',
|
|
167
|
+
javascript: 'generated.js',
|
|
168
|
+
python: 'generated.py',
|
|
169
|
+
rust: 'generated.rs',
|
|
170
|
+
go: 'generated.go',
|
|
171
|
+
java: 'Generated.java',
|
|
172
|
+
csharp: 'Generated.cs',
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
return extensions[language] || 'generated.txt';
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review Command - Code review with AI
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import { Marked } from 'marked';
|
|
8
|
+
import { markedTerminal } from 'marked-terminal';
|
|
9
|
+
import { Config } from '../utils/config.js';
|
|
10
|
+
import { Logger } from '../utils/logger.js';
|
|
11
|
+
import { APIClient } from '../utils/api.js';
|
|
12
|
+
import { FileUtils } from '../utils/files.js';
|
|
13
|
+
|
|
14
|
+
interface ReviewOptions {
|
|
15
|
+
format: 'text' | 'json' | 'markdown';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class ReviewCommand {
|
|
19
|
+
private config: Config;
|
|
20
|
+
private logger: Logger;
|
|
21
|
+
private api: APIClient;
|
|
22
|
+
private fileUtils: FileUtils;
|
|
23
|
+
private marked: Marked;
|
|
24
|
+
|
|
25
|
+
constructor(config: Config, logger: Logger) {
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.logger = logger;
|
|
28
|
+
this.api = new APIClient(config, logger);
|
|
29
|
+
this.fileUtils = new FileUtils(process.cwd(), config.get('project').ignorePatterns);
|
|
30
|
+
|
|
31
|
+
this.marked = new Marked();
|
|
32
|
+
this.marked.use(markedTerminal() as any);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async run(filePath: string, options: ReviewOptions): Promise<void> {
|
|
36
|
+
// Check auth
|
|
37
|
+
if (!this.config.isAuthenticated()) {
|
|
38
|
+
this.logger.error('Not authenticated. Run: vigthoria login');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Read file
|
|
43
|
+
const file = this.fileUtils.readFile(filePath);
|
|
44
|
+
if (!file) {
|
|
45
|
+
this.logger.error(`File not found: ${filePath}`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.logger.section(`Reviewing: ${file.relativePath}`);
|
|
50
|
+
console.log(chalk.gray(`Language: ${file.language} | Lines: ${file.lines}`));
|
|
51
|
+
console.log();
|
|
52
|
+
|
|
53
|
+
const spinner = ora({
|
|
54
|
+
text: 'Analyzing code quality...',
|
|
55
|
+
spinner: 'dots',
|
|
56
|
+
}).start();
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const review = await this.api.reviewCode(file.content, file.language);
|
|
60
|
+
|
|
61
|
+
spinner.stop();
|
|
62
|
+
|
|
63
|
+
// Format output
|
|
64
|
+
switch (options.format) {
|
|
65
|
+
case 'json':
|
|
66
|
+
console.log(JSON.stringify(review, null, 2));
|
|
67
|
+
break;
|
|
68
|
+
case 'markdown':
|
|
69
|
+
this.printMarkdownReview(review, file.relativePath);
|
|
70
|
+
break;
|
|
71
|
+
case 'text':
|
|
72
|
+
default:
|
|
73
|
+
this.printTextReview(review);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
} catch (error) {
|
|
78
|
+
spinner.stop();
|
|
79
|
+
this.logger.error('Review failed:', (error as Error).message);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private printTextReview(review: {
|
|
84
|
+
score: number;
|
|
85
|
+
issues: { type: string; line: number; message: string; severity: string }[];
|
|
86
|
+
suggestions: string[];
|
|
87
|
+
}): void {
|
|
88
|
+
// Score
|
|
89
|
+
const scoreColor = review.score >= 80 ? chalk.green : review.score >= 60 ? chalk.yellow : chalk.red;
|
|
90
|
+
console.log();
|
|
91
|
+
console.log(chalk.bold('Quality Score: ') + scoreColor(`${review.score}/100`));
|
|
92
|
+
console.log(this.renderScoreBar(review.score));
|
|
93
|
+
console.log();
|
|
94
|
+
|
|
95
|
+
// Issues
|
|
96
|
+
if (review.issues.length > 0) {
|
|
97
|
+
this.logger.section(`Issues (${review.issues.length})`);
|
|
98
|
+
|
|
99
|
+
review.issues.forEach((issue, i) => {
|
|
100
|
+
const severityIcon = this.getSeverityIcon(issue.severity);
|
|
101
|
+
const severityColor = this.getSeverityColor(issue.severity);
|
|
102
|
+
|
|
103
|
+
console.log(
|
|
104
|
+
severityColor(`${severityIcon} [${issue.type}]`) +
|
|
105
|
+
chalk.gray(` Line ${issue.line}:`) +
|
|
106
|
+
` ${issue.message}`
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
console.log();
|
|
110
|
+
} else {
|
|
111
|
+
this.logger.success('No issues found!');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Suggestions
|
|
115
|
+
if (review.suggestions.length > 0) {
|
|
116
|
+
this.logger.section('Suggestions');
|
|
117
|
+
|
|
118
|
+
review.suggestions.forEach((suggestion, i) => {
|
|
119
|
+
console.log(chalk.cyan(`${i + 1}.`) + ` ${suggestion}`);
|
|
120
|
+
});
|
|
121
|
+
console.log();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private printMarkdownReview(review: {
|
|
126
|
+
score: number;
|
|
127
|
+
issues: { type: string; line: number; message: string; severity: string }[];
|
|
128
|
+
suggestions: string[];
|
|
129
|
+
}, filePath: string): void {
|
|
130
|
+
let markdown = `# Code Review: ${filePath}\n\n`;
|
|
131
|
+
markdown += `## Score: ${review.score}/100\n\n`;
|
|
132
|
+
|
|
133
|
+
if (review.issues.length > 0) {
|
|
134
|
+
markdown += `## Issues\n\n`;
|
|
135
|
+
review.issues.forEach(issue => {
|
|
136
|
+
markdown += `- **[${issue.severity.toUpperCase()}]** Line ${issue.line}: ${issue.message} (${issue.type})\n`;
|
|
137
|
+
});
|
|
138
|
+
markdown += '\n';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (review.suggestions.length > 0) {
|
|
142
|
+
markdown += `## Suggestions\n\n`;
|
|
143
|
+
review.suggestions.forEach((s, i) => {
|
|
144
|
+
markdown += `${i + 1}. ${s}\n`;
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.log(this.marked.parse(markdown));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private renderScoreBar(score: number): string {
|
|
152
|
+
const width = 30;
|
|
153
|
+
const filled = Math.round((score / 100) * width);
|
|
154
|
+
const empty = width - filled;
|
|
155
|
+
|
|
156
|
+
const color = score >= 80 ? chalk.green : score >= 60 ? chalk.yellow : chalk.red;
|
|
157
|
+
|
|
158
|
+
return color('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private getSeverityIcon(severity: string): string {
|
|
162
|
+
switch (severity.toLowerCase()) {
|
|
163
|
+
case 'error':
|
|
164
|
+
return '✗';
|
|
165
|
+
case 'warning':
|
|
166
|
+
return '⚠';
|
|
167
|
+
case 'info':
|
|
168
|
+
return 'ℹ';
|
|
169
|
+
default:
|
|
170
|
+
return '•';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private getSeverityColor(severity: string): typeof chalk {
|
|
175
|
+
switch (severity.toLowerCase()) {
|
|
176
|
+
case 'error':
|
|
177
|
+
return chalk.red;
|
|
178
|
+
case 'warning':
|
|
179
|
+
return chalk.yellow;
|
|
180
|
+
case 'info':
|
|
181
|
+
return chalk.blue;
|
|
182
|
+
default:
|
|
183
|
+
return chalk.white;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Vigthoria CLI - AI-Powered Terminal Coding Assistant
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* vigthoria chat - Start interactive chat
|
|
7
|
+
* vigthoria edit <file> - Edit a file with AI assistance
|
|
8
|
+
* vigthoria generate <desc> - Generate code from description
|
|
9
|
+
* vigthoria explain <file> - Explain code in a file
|
|
10
|
+
* vigthoria fix <file> - Fix issues in a file
|
|
11
|
+
* vigthoria review <file> - Review code quality
|
|
12
|
+
* vigthoria login - Authenticate with Vigthoria
|
|
13
|
+
* vigthoria config - Configure settings
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { Command } from 'commander';
|
|
17
|
+
import { ChatCommand } from './commands/chat.js';
|
|
18
|
+
import { EditCommand } from './commands/edit.js';
|
|
19
|
+
import { GenerateCommand } from './commands/generate.js';
|
|
20
|
+
import { ExplainCommand } from './commands/explain.js';
|
|
21
|
+
import { AuthCommand } from './commands/auth.js';
|
|
22
|
+
import { ConfigCommand } from './commands/config.js';
|
|
23
|
+
import { ReviewCommand } from './commands/review.js';
|
|
24
|
+
import { Config } from './utils/config.js';
|
|
25
|
+
import { Logger } from './utils/logger.js';
|
|
26
|
+
import chalk from 'chalk';
|
|
27
|
+
|
|
28
|
+
const VERSION = '1.0.0';
|
|
29
|
+
|
|
30
|
+
async function main() {
|
|
31
|
+
const program = new Command();
|
|
32
|
+
const config = new Config();
|
|
33
|
+
const logger = new Logger();
|
|
34
|
+
|
|
35
|
+
// Banner - Fixed alignment with proper padding
|
|
36
|
+
const boxWidth = 61; // Inner content width
|
|
37
|
+
const titleText = 'VIGTHORIA CLI - AI-Powered Coding Assistant';
|
|
38
|
+
const versionText = `Version ${VERSION}`;
|
|
39
|
+
|
|
40
|
+
// Calculate padding for centering
|
|
41
|
+
const titlePad = Math.floor((boxWidth - 4 - titleText.length) / 2);
|
|
42
|
+
const versionPad = Math.floor((boxWidth - 4 - versionText.length) / 2);
|
|
43
|
+
|
|
44
|
+
console.log(chalk.cyan('╔' + '═'.repeat(boxWidth) + '╗'));
|
|
45
|
+
console.log(chalk.cyan('║' + ' '.repeat(boxWidth) + '║'));
|
|
46
|
+
console.log(chalk.cyan('║') + ' '.repeat(titlePad) + chalk.bold.white('VIGTHORIA CLI') + chalk.cyan(' - AI-Powered Coding Assistant') + ' '.repeat(boxWidth - titlePad - titleText.length) + chalk.cyan('║'));
|
|
47
|
+
console.log(chalk.cyan('║') + ' '.repeat(versionPad) + chalk.gray(versionText) + ' '.repeat(boxWidth - versionPad - versionText.length) + chalk.cyan('║'));
|
|
48
|
+
console.log(chalk.cyan('║' + ' '.repeat(boxWidth) + '║'));
|
|
49
|
+
console.log(chalk.cyan('╚' + '═'.repeat(boxWidth) + '╝'));
|
|
50
|
+
console.log();
|
|
51
|
+
|
|
52
|
+
program
|
|
53
|
+
.name('vigthoria')
|
|
54
|
+
.description('AI-powered terminal coding assistant for Vigthoria Coder subscribers')
|
|
55
|
+
.version(VERSION);
|
|
56
|
+
|
|
57
|
+
// Chat command - Interactive mode
|
|
58
|
+
program
|
|
59
|
+
.command('chat')
|
|
60
|
+
.alias('c')
|
|
61
|
+
.description('Start interactive chat with Vigthoria AI')
|
|
62
|
+
.option('-m, --model <model>', 'Select AI model (fast, balanced, code, creative)', 'balanced')
|
|
63
|
+
.option('-p, --project <path>', 'Set project context path', process.cwd())
|
|
64
|
+
.option('-a, --agent', 'Enable agentic mode (Claude Code-like autonomous actions)', false)
|
|
65
|
+
.option('-r, --resume', 'Resume last session for this project', false)
|
|
66
|
+
.option('-l, --local', 'Local mode: use Ollama directly without auth', false)
|
|
67
|
+
.option('--auto-approve', 'Auto-approve agent actions (dangerous!)', false)
|
|
68
|
+
.action(async (options) => {
|
|
69
|
+
const chat = new ChatCommand(config, logger);
|
|
70
|
+
await chat.run({
|
|
71
|
+
model: options.model,
|
|
72
|
+
project: options.project,
|
|
73
|
+
agent: options.agent,
|
|
74
|
+
autoApprove: options.autoApprove,
|
|
75
|
+
resume: options.resume,
|
|
76
|
+
local: options.local,
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Agent command - Agentic mode (Claude Code-like)
|
|
81
|
+
program
|
|
82
|
+
.command('agent')
|
|
83
|
+
.alias('a')
|
|
84
|
+
.description('Start agentic mode - AI can read/write files, run commands')
|
|
85
|
+
.option('-m, --model <model>', 'Select AI model', 'code')
|
|
86
|
+
.option('-p, --project <path>', 'Set project context path', process.cwd())
|
|
87
|
+
.option('-l, --local', 'Local mode: use Ollama directly without auth', false)
|
|
88
|
+
.option('--auto-approve', 'Auto-approve all actions (dangerous!)', false)
|
|
89
|
+
.action(async (options) => {
|
|
90
|
+
const chat = new ChatCommand(config, logger);
|
|
91
|
+
await chat.run({
|
|
92
|
+
model: options.model,
|
|
93
|
+
project: options.project,
|
|
94
|
+
agent: true,
|
|
95
|
+
autoApprove: options.autoApprove,
|
|
96
|
+
local: options.local,
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Edit command - Edit files with AI
|
|
101
|
+
program
|
|
102
|
+
.command('edit <file>')
|
|
103
|
+
.alias('e')
|
|
104
|
+
.description('Edit a file with AI assistance')
|
|
105
|
+
.option('-i, --instruction <text>', 'Editing instruction')
|
|
106
|
+
.option('-m, --model <model>', 'Select AI model', 'code')
|
|
107
|
+
.action(async (file, options) => {
|
|
108
|
+
const edit = new EditCommand(config, logger);
|
|
109
|
+
await edit.run(file, options);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Generate command - Generate code
|
|
113
|
+
program
|
|
114
|
+
.command('generate <description>')
|
|
115
|
+
.alias('g')
|
|
116
|
+
.description('Generate code from description')
|
|
117
|
+
.option('-l, --language <lang>', 'Target language', 'typescript')
|
|
118
|
+
.option('-o, --output <file>', 'Output file path')
|
|
119
|
+
.option('-m, --model <model>', 'Select AI model', 'code')
|
|
120
|
+
.action(async (description, options) => {
|
|
121
|
+
const generate = new GenerateCommand(config, logger);
|
|
122
|
+
await generate.run(description, options);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Explain command - Explain code
|
|
126
|
+
program
|
|
127
|
+
.command('explain <file>')
|
|
128
|
+
.alias('x')
|
|
129
|
+
.description('Explain code in a file')
|
|
130
|
+
.option('-l, --lines <range>', 'Line range (e.g., 1-50)')
|
|
131
|
+
.option('-d, --detail <level>', 'Detail level (brief, normal, detailed)', 'normal')
|
|
132
|
+
.action(async (file, options) => {
|
|
133
|
+
const explain = new ExplainCommand(config, logger);
|
|
134
|
+
await explain.run(file, options);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Fix command - Fix code issues
|
|
138
|
+
program
|
|
139
|
+
.command('fix <file>')
|
|
140
|
+
.alias('f')
|
|
141
|
+
.description('Fix issues in a file')
|
|
142
|
+
.option('-t, --type <type>', 'Fix type (bugs, style, security, performance)', 'bugs')
|
|
143
|
+
.option('--apply', 'Automatically apply fixes', false)
|
|
144
|
+
.action(async (file, options) => {
|
|
145
|
+
const edit = new EditCommand(config, logger);
|
|
146
|
+
await edit.fix(file, options);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Review command - Code review
|
|
150
|
+
program
|
|
151
|
+
.command('review <file>')
|
|
152
|
+
.alias('r')
|
|
153
|
+
.description('Review code quality')
|
|
154
|
+
.option('-f, --format <format>', 'Output format (text, json, markdown)', 'text')
|
|
155
|
+
.action(async (file, options) => {
|
|
156
|
+
const review = new ReviewCommand(config, logger);
|
|
157
|
+
await review.run(file, options);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Auth commands
|
|
161
|
+
program
|
|
162
|
+
.command('login')
|
|
163
|
+
.description('Login to Vigthoria Coder')
|
|
164
|
+
.option('-t, --token <token>', 'API token')
|
|
165
|
+
.action(async (options) => {
|
|
166
|
+
const auth = new AuthCommand(config, logger);
|
|
167
|
+
await auth.login(options);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
program
|
|
171
|
+
.command('logout')
|
|
172
|
+
.description('Logout from Vigthoria Coder')
|
|
173
|
+
.action(async () => {
|
|
174
|
+
const auth = new AuthCommand(config, logger);
|
|
175
|
+
await auth.logout();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
program
|
|
179
|
+
.command('status')
|
|
180
|
+
.description('Show authentication and subscription status')
|
|
181
|
+
.action(async () => {
|
|
182
|
+
const auth = new AuthCommand(config, logger);
|
|
183
|
+
await auth.status();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Config command
|
|
187
|
+
program
|
|
188
|
+
.command('config')
|
|
189
|
+
.description('Configure Vigthoria CLI settings')
|
|
190
|
+
.option('-s, --set <key=value>', 'Set a configuration value')
|
|
191
|
+
.option('-g, --get <key>', 'Get a configuration value')
|
|
192
|
+
.option('-l, --list', 'List all settings')
|
|
193
|
+
.option('-r, --reset', 'Reset to defaults')
|
|
194
|
+
.action(async (options) => {
|
|
195
|
+
const configCmd = new ConfigCommand(config, logger);
|
|
196
|
+
await configCmd.run(options);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Init command - Initialize project
|
|
200
|
+
program
|
|
201
|
+
.command('init')
|
|
202
|
+
.description('Initialize Vigthoria in current project')
|
|
203
|
+
.action(async () => {
|
|
204
|
+
const configCmd = new ConfigCommand(config, logger);
|
|
205
|
+
await configCmd.init();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Default to chat if no command
|
|
209
|
+
if (process.argv.length === 2) {
|
|
210
|
+
const chat = new ChatCommand(config, logger);
|
|
211
|
+
await chat.run({ model: 'code', project: process.cwd() });
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
await program.parseAsync(process.argv);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
main().catch((err) => {
|
|
219
|
+
console.error(chalk.red('Error:'), err.message);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
});
|