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,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Manager for Vigthoria CLI
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import Conf from 'conf';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
|
|
9
|
+
export interface VigthoriaCLIConfig {
|
|
10
|
+
apiUrl: string;
|
|
11
|
+
wsUrl: string;
|
|
12
|
+
authToken: string | null;
|
|
13
|
+
refreshToken: string | null;
|
|
14
|
+
userId: string | null;
|
|
15
|
+
email: string | null;
|
|
16
|
+
subscription: {
|
|
17
|
+
plan: string | null;
|
|
18
|
+
status: string | null;
|
|
19
|
+
expiresAt: string | null;
|
|
20
|
+
};
|
|
21
|
+
preferences: {
|
|
22
|
+
defaultModel: string;
|
|
23
|
+
theme: 'dark' | 'light';
|
|
24
|
+
autoApplyFixes: boolean;
|
|
25
|
+
showDiffs: boolean;
|
|
26
|
+
contextLines: number;
|
|
27
|
+
maxTokens: number;
|
|
28
|
+
};
|
|
29
|
+
project: {
|
|
30
|
+
rootPath: string | null;
|
|
31
|
+
ignorePatterns: string[];
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const defaultConfig: VigthoriaCLIConfig = {
|
|
36
|
+
apiUrl: 'https://coder.vigthoria.io',
|
|
37
|
+
wsUrl: 'wss://coder.vigthoria.io/ws',
|
|
38
|
+
authToken: null,
|
|
39
|
+
refreshToken: null,
|
|
40
|
+
userId: null,
|
|
41
|
+
email: null,
|
|
42
|
+
subscription: {
|
|
43
|
+
plan: null,
|
|
44
|
+
status: null,
|
|
45
|
+
expiresAt: null,
|
|
46
|
+
},
|
|
47
|
+
preferences: {
|
|
48
|
+
defaultModel: 'vigthoria-code',
|
|
49
|
+
theme: 'dark',
|
|
50
|
+
autoApplyFixes: false,
|
|
51
|
+
showDiffs: true,
|
|
52
|
+
contextLines: 3,
|
|
53
|
+
maxTokens: 4096,
|
|
54
|
+
},
|
|
55
|
+
project: {
|
|
56
|
+
rootPath: null,
|
|
57
|
+
ignorePatterns: [
|
|
58
|
+
'node_modules',
|
|
59
|
+
'.git',
|
|
60
|
+
'dist',
|
|
61
|
+
'build',
|
|
62
|
+
'__pycache__',
|
|
63
|
+
'.venv',
|
|
64
|
+
'venv',
|
|
65
|
+
'.env',
|
|
66
|
+
'*.log',
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export class Config {
|
|
72
|
+
private store: Conf<VigthoriaCLIConfig>;
|
|
73
|
+
|
|
74
|
+
constructor() {
|
|
75
|
+
this.store = new Conf<VigthoriaCLIConfig>({
|
|
76
|
+
projectName: 'vigthoria-cli',
|
|
77
|
+
defaults: defaultConfig,
|
|
78
|
+
schema: {
|
|
79
|
+
apiUrl: { type: 'string' },
|
|
80
|
+
wsUrl: { type: 'string' },
|
|
81
|
+
authToken: { type: ['string', 'null'] },
|
|
82
|
+
refreshToken: { type: ['string', 'null'] },
|
|
83
|
+
userId: { type: ['string', 'null'] },
|
|
84
|
+
email: { type: ['string', 'null'] },
|
|
85
|
+
subscription: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: {
|
|
88
|
+
plan: { type: ['string', 'null'] },
|
|
89
|
+
status: { type: ['string', 'null'] },
|
|
90
|
+
expiresAt: { type: ['string', 'null'] },
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
preferences: {
|
|
94
|
+
type: 'object',
|
|
95
|
+
properties: {
|
|
96
|
+
defaultModel: { type: 'string' },
|
|
97
|
+
theme: { type: 'string', enum: ['dark', 'light'] },
|
|
98
|
+
autoApplyFixes: { type: 'boolean' },
|
|
99
|
+
showDiffs: { type: 'boolean' },
|
|
100
|
+
contextLines: { type: 'number' },
|
|
101
|
+
maxTokens: { type: 'number' },
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
project: {
|
|
105
|
+
type: 'object',
|
|
106
|
+
properties: {
|
|
107
|
+
rootPath: { type: ['string', 'null'] },
|
|
108
|
+
ignorePatterns: { type: 'array', items: { type: 'string' } },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
get<K extends keyof VigthoriaCLIConfig>(key: K): VigthoriaCLIConfig[K] {
|
|
116
|
+
return this.store.get(key);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
set<K extends keyof VigthoriaCLIConfig>(key: K, value: VigthoriaCLIConfig[K]): void {
|
|
120
|
+
this.store.set(key, value);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
getAll(): VigthoriaCLIConfig {
|
|
124
|
+
return this.store.store;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
reset(): void {
|
|
128
|
+
this.store.clear();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
isAuthenticated(): boolean {
|
|
132
|
+
return this.store.get('authToken') !== null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
hasValidSubscription(): boolean {
|
|
136
|
+
const sub = this.store.get('subscription');
|
|
137
|
+
if (!sub.status || sub.status !== 'active') return false;
|
|
138
|
+
if (sub.expiresAt) {
|
|
139
|
+
const expires = new Date(sub.expiresAt);
|
|
140
|
+
return expires > new Date();
|
|
141
|
+
}
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getConfigPath(): string {
|
|
146
|
+
return this.store.path;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Auth helpers
|
|
150
|
+
setAuth(data: {
|
|
151
|
+
token: string;
|
|
152
|
+
refreshToken?: string;
|
|
153
|
+
userId: string;
|
|
154
|
+
email: string;
|
|
155
|
+
}): void {
|
|
156
|
+
this.store.set('authToken', data.token);
|
|
157
|
+
if (data.refreshToken) {
|
|
158
|
+
this.store.set('refreshToken', data.refreshToken);
|
|
159
|
+
}
|
|
160
|
+
this.store.set('userId', data.userId);
|
|
161
|
+
this.store.set('email', data.email);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
clearAuth(): void {
|
|
165
|
+
this.store.set('authToken', null);
|
|
166
|
+
this.store.set('refreshToken', null);
|
|
167
|
+
this.store.set('userId', null);
|
|
168
|
+
this.store.set('email', null);
|
|
169
|
+
this.store.set('subscription', {
|
|
170
|
+
plan: null,
|
|
171
|
+
status: null,
|
|
172
|
+
expiresAt: null,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
setSubscription(data: {
|
|
177
|
+
plan: string;
|
|
178
|
+
status: string;
|
|
179
|
+
expiresAt?: string;
|
|
180
|
+
}): void {
|
|
181
|
+
this.store.set('subscription', {
|
|
182
|
+
plan: data.plan,
|
|
183
|
+
status: data.status,
|
|
184
|
+
expiresAt: data.expiresAt || null,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Model selection - Native Vigthoria Models
|
|
189
|
+
getAvailableModels(): { id: string; name: string; description: string }[] {
|
|
190
|
+
const sub = this.store.get('subscription');
|
|
191
|
+
|
|
192
|
+
// All users get access to fast models
|
|
193
|
+
const models = [
|
|
194
|
+
{ id: 'mini', name: 'Vigthoria Mini 0.6B', description: 'Ultra-fast, simple tasks' },
|
|
195
|
+
{ id: 'fast', name: 'Vigthoria Fast 1.7B', description: 'Quick responses, streaming' },
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
// Developer+ plans get balanced model
|
|
199
|
+
if (sub.plan && sub.plan !== 'free') {
|
|
200
|
+
models.push(
|
|
201
|
+
{ id: 'balanced', name: 'Vigthoria Balanced 4B', description: 'General purpose' },
|
|
202
|
+
{ id: 'music', name: 'Vigthoria Music 4B', description: 'Music production & theory' },
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Pro plans get code and creative models
|
|
207
|
+
if (sub.plan === 'developer' || sub.plan === 'pro' || sub.plan === 'ultra' || sub.plan === 'enterprise') {
|
|
208
|
+
models.push(
|
|
209
|
+
{ id: 'code', name: 'Vigthoria Code v2 8B', description: 'Code specialist (training)' },
|
|
210
|
+
{ id: 'creative', name: 'Vigthoria Creative 9B v4', description: 'Creative writing, lyrics' },
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// External model aliases
|
|
215
|
+
if (sub.plan === 'pro' || sub.plan === 'ultra' || sub.plan === 'enterprise') {
|
|
216
|
+
models.push(
|
|
217
|
+
{ id: 'qwen-coder', name: 'Qwen 2.5 Coder 7B', description: 'External code model' },
|
|
218
|
+
{ id: 'deepseek', name: 'DeepSeek Coder v2', description: 'External code model' },
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return models;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File utilities for Vigthoria CLI
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { glob } from 'glob';
|
|
8
|
+
|
|
9
|
+
export interface FileInfo {
|
|
10
|
+
path: string;
|
|
11
|
+
relativePath: string;
|
|
12
|
+
content: string;
|
|
13
|
+
language: string;
|
|
14
|
+
lines: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class FileUtils {
|
|
18
|
+
private projectRoot: string;
|
|
19
|
+
private ignorePatterns: string[];
|
|
20
|
+
|
|
21
|
+
constructor(projectRoot: string, ignorePatterns: string[] = []) {
|
|
22
|
+
this.projectRoot = projectRoot;
|
|
23
|
+
this.ignorePatterns = ignorePatterns;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Read file with metadata
|
|
27
|
+
readFile(filePath: string): FileInfo | null {
|
|
28
|
+
try {
|
|
29
|
+
const absolutePath = path.isAbsolute(filePath)
|
|
30
|
+
? filePath
|
|
31
|
+
: path.join(this.projectRoot, filePath);
|
|
32
|
+
|
|
33
|
+
const content = fs.readFileSync(absolutePath, 'utf-8');
|
|
34
|
+
const relativePath = path.relative(this.projectRoot, absolutePath);
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
path: absolutePath,
|
|
38
|
+
relativePath,
|
|
39
|
+
content,
|
|
40
|
+
language: this.detectLanguage(absolutePath),
|
|
41
|
+
lines: content.split('\n').length,
|
|
42
|
+
};
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Read specific lines
|
|
49
|
+
readLines(filePath: string, start: number, end: number): string | null {
|
|
50
|
+
const file = this.readFile(filePath);
|
|
51
|
+
if (!file) return null;
|
|
52
|
+
|
|
53
|
+
const lines = file.content.split('\n');
|
|
54
|
+
return lines.slice(start - 1, end).join('\n');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Write file
|
|
58
|
+
writeFile(filePath: string, content: string): boolean {
|
|
59
|
+
try {
|
|
60
|
+
const absolutePath = path.isAbsolute(filePath)
|
|
61
|
+
? filePath
|
|
62
|
+
: path.join(this.projectRoot, filePath);
|
|
63
|
+
|
|
64
|
+
// Create directory if needed
|
|
65
|
+
const dir = path.dirname(absolutePath);
|
|
66
|
+
if (!fs.existsSync(dir)) {
|
|
67
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fs.writeFileSync(absolutePath, content, 'utf-8');
|
|
71
|
+
return true;
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Backup file
|
|
78
|
+
backupFile(filePath: string): string | null {
|
|
79
|
+
const file = this.readFile(filePath);
|
|
80
|
+
if (!file) return null;
|
|
81
|
+
|
|
82
|
+
const backupPath = `${file.path}.bak.${Date.now()}`;
|
|
83
|
+
if (this.writeFile(backupPath, file.content)) {
|
|
84
|
+
return backupPath;
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// List project files
|
|
90
|
+
async listFiles(pattern: string = '**/*'): Promise<string[]> {
|
|
91
|
+
const files = await glob(pattern, {
|
|
92
|
+
cwd: this.projectRoot,
|
|
93
|
+
ignore: this.ignorePatterns,
|
|
94
|
+
nodir: true,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return files;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Detect language from file extension
|
|
101
|
+
detectLanguage(filePath: string): string {
|
|
102
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
103
|
+
const languageMap: Record<string, string> = {
|
|
104
|
+
'.ts': 'typescript',
|
|
105
|
+
'.tsx': 'typescript',
|
|
106
|
+
'.js': 'javascript',
|
|
107
|
+
'.jsx': 'javascript',
|
|
108
|
+
'.py': 'python',
|
|
109
|
+
'.rs': 'rust',
|
|
110
|
+
'.go': 'go',
|
|
111
|
+
'.java': 'java',
|
|
112
|
+
'.c': 'c',
|
|
113
|
+
'.cpp': 'cpp',
|
|
114
|
+
'.h': 'c',
|
|
115
|
+
'.hpp': 'cpp',
|
|
116
|
+
'.cs': 'csharp',
|
|
117
|
+
'.rb': 'ruby',
|
|
118
|
+
'.php': 'php',
|
|
119
|
+
'.swift': 'swift',
|
|
120
|
+
'.kt': 'kotlin',
|
|
121
|
+
'.scala': 'scala',
|
|
122
|
+
'.sql': 'sql',
|
|
123
|
+
'.html': 'html',
|
|
124
|
+
'.css': 'css',
|
|
125
|
+
'.scss': 'scss',
|
|
126
|
+
'.less': 'less',
|
|
127
|
+
'.json': 'json',
|
|
128
|
+
'.yaml': 'yaml',
|
|
129
|
+
'.yml': 'yaml',
|
|
130
|
+
'.xml': 'xml',
|
|
131
|
+
'.md': 'markdown',
|
|
132
|
+
'.sh': 'bash',
|
|
133
|
+
'.bash': 'bash',
|
|
134
|
+
'.zsh': 'zsh',
|
|
135
|
+
'.dockerfile': 'dockerfile',
|
|
136
|
+
'.vue': 'vue',
|
|
137
|
+
'.svelte': 'svelte',
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return languageMap[ext] || 'text';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Get project context
|
|
144
|
+
async getProjectContext(maxFiles: number = 20): Promise<{
|
|
145
|
+
type: string;
|
|
146
|
+
files: string[];
|
|
147
|
+
dependencies: Record<string, string>;
|
|
148
|
+
}> {
|
|
149
|
+
const context = {
|
|
150
|
+
type: 'unknown',
|
|
151
|
+
files: [] as string[],
|
|
152
|
+
dependencies: {} as Record<string, string>,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Detect project type
|
|
156
|
+
if (fs.existsSync(path.join(this.projectRoot, 'package.json'))) {
|
|
157
|
+
context.type = 'node';
|
|
158
|
+
const pkg = JSON.parse(fs.readFileSync(
|
|
159
|
+
path.join(this.projectRoot, 'package.json'),
|
|
160
|
+
'utf-8'
|
|
161
|
+
));
|
|
162
|
+
context.dependencies = {
|
|
163
|
+
...pkg.dependencies,
|
|
164
|
+
...pkg.devDependencies,
|
|
165
|
+
};
|
|
166
|
+
} else if (fs.existsSync(path.join(this.projectRoot, 'requirements.txt'))) {
|
|
167
|
+
context.type = 'python';
|
|
168
|
+
} else if (fs.existsSync(path.join(this.projectRoot, 'Cargo.toml'))) {
|
|
169
|
+
context.type = 'rust';
|
|
170
|
+
} else if (fs.existsSync(path.join(this.projectRoot, 'go.mod'))) {
|
|
171
|
+
context.type = 'go';
|
|
172
|
+
} else if (fs.existsSync(path.join(this.projectRoot, 'pom.xml'))) {
|
|
173
|
+
context.type = 'java-maven';
|
|
174
|
+
} else if (fs.existsSync(path.join(this.projectRoot, 'build.gradle'))) {
|
|
175
|
+
context.type = 'java-gradle';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Get relevant files
|
|
179
|
+
const allFiles = await this.listFiles();
|
|
180
|
+
context.files = allFiles.slice(0, maxFiles);
|
|
181
|
+
|
|
182
|
+
return context;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Create diff
|
|
186
|
+
createDiff(original: string, modified: string): { added: string[]; removed: string[] } {
|
|
187
|
+
const originalLines = original.split('\n');
|
|
188
|
+
const modifiedLines = modified.split('\n');
|
|
189
|
+
|
|
190
|
+
const added: string[] = [];
|
|
191
|
+
const removed: string[] = [];
|
|
192
|
+
|
|
193
|
+
// Simple diff - for display purposes
|
|
194
|
+
const maxLen = Math.max(originalLines.length, modifiedLines.length);
|
|
195
|
+
|
|
196
|
+
for (let i = 0; i < maxLen; i++) {
|
|
197
|
+
const origLine = originalLines[i];
|
|
198
|
+
const modLine = modifiedLines[i];
|
|
199
|
+
|
|
200
|
+
if (origLine !== modLine) {
|
|
201
|
+
if (origLine !== undefined) {
|
|
202
|
+
removed.push(`${i + 1}: ${origLine}`);
|
|
203
|
+
}
|
|
204
|
+
if (modLine !== undefined) {
|
|
205
|
+
added.push(`${i + 1}: ${modLine}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return { added, removed };
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger utility for Vigthoria CLI
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
|
|
7
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'success';
|
|
8
|
+
|
|
9
|
+
export class Logger {
|
|
10
|
+
private verbose: boolean = false;
|
|
11
|
+
|
|
12
|
+
setVerbose(verbose: boolean): void {
|
|
13
|
+
this.verbose = verbose;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
debug(...args: unknown[]): void {
|
|
17
|
+
if (this.verbose) {
|
|
18
|
+
console.log(chalk.gray('[DEBUG]'), ...args);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
info(...args: unknown[]): void {
|
|
23
|
+
console.log(chalk.blue('ℹ'), ...args);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
warn(...args: unknown[]): void {
|
|
27
|
+
console.log(chalk.yellow('⚠'), ...args);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
error(...args: unknown[]): void {
|
|
31
|
+
console.error(chalk.red('✗'), ...args);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
success(...args: unknown[]): void {
|
|
35
|
+
console.log(chalk.green('✓'), ...args);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// AI response formatting
|
|
39
|
+
ai(message: string): void {
|
|
40
|
+
console.log(chalk.cyan('🤖'), message);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// User input formatting
|
|
44
|
+
user(message: string): void {
|
|
45
|
+
console.log(chalk.white('👤'), message);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Code block
|
|
49
|
+
code(code: string, language?: string): void {
|
|
50
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
51
|
+
console.log(chalk.yellow(code));
|
|
52
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Diff output
|
|
56
|
+
diff(added: string[], removed: string[]): void {
|
|
57
|
+
removed.forEach(line => console.log(chalk.red(`- ${line}`)));
|
|
58
|
+
added.forEach(line => console.log(chalk.green(`+ ${line}`)));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Section header
|
|
62
|
+
section(title: string): void {
|
|
63
|
+
console.log();
|
|
64
|
+
console.log(chalk.bold.cyan(`═══ ${title} ═══`));
|
|
65
|
+
console.log();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Progress
|
|
69
|
+
progress(message: string): void {
|
|
70
|
+
process.stdout.write(chalk.gray(`${message}\r`));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Clear line
|
|
74
|
+
clearLine(): void {
|
|
75
|
+
process.stdout.write('\r\x1b[K');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Box output
|
|
79
|
+
box(content: string, title?: string): void {
|
|
80
|
+
const lines = content.split('\n');
|
|
81
|
+
const maxLen = Math.max(...lines.map(l => l.length), title?.length || 0);
|
|
82
|
+
const width = maxLen + 4;
|
|
83
|
+
|
|
84
|
+
console.log(chalk.cyan('┌' + '─'.repeat(width - 2) + '┐'));
|
|
85
|
+
if (title) {
|
|
86
|
+
console.log(chalk.cyan('│ ') + chalk.bold.white(title.padEnd(width - 4)) + chalk.cyan(' │'));
|
|
87
|
+
console.log(chalk.cyan('├' + '─'.repeat(width - 2) + '┤'));
|
|
88
|
+
}
|
|
89
|
+
lines.forEach(line => {
|
|
90
|
+
console.log(chalk.cyan('│ ') + line.padEnd(width - 4) + chalk.cyan(' │'));
|
|
91
|
+
});
|
|
92
|
+
console.log(chalk.cyan('└' + '─'.repeat(width - 2) + '┘'));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Table output
|
|
96
|
+
table(headers: string[], rows: string[][]): void {
|
|
97
|
+
const colWidths = headers.map((h, i) => {
|
|
98
|
+
const maxData = Math.max(...rows.map(r => (r[i] || '').length));
|
|
99
|
+
return Math.max(h.length, maxData);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Header
|
|
103
|
+
console.log(
|
|
104
|
+
chalk.gray('│ ') +
|
|
105
|
+
headers.map((h, i) => chalk.bold(h.padEnd(colWidths[i]))).join(chalk.gray(' │ ')) +
|
|
106
|
+
chalk.gray(' │')
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Separator
|
|
110
|
+
console.log(
|
|
111
|
+
chalk.gray('├─') +
|
|
112
|
+
colWidths.map(w => '─'.repeat(w)).join(chalk.gray('─┼─')) +
|
|
113
|
+
chalk.gray('─┤')
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Rows
|
|
117
|
+
rows.forEach(row => {
|
|
118
|
+
console.log(
|
|
119
|
+
chalk.gray('│ ') +
|
|
120
|
+
row.map((cell, i) => (cell || '').padEnd(colWidths[i])).join(chalk.gray(' │ ')) +
|
|
121
|
+
chalk.gray(' │')
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|