vigthoria-cli 1.6.1 → 1.6.4

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.
Files changed (46) hide show
  1. package/README.md +52 -1
  2. package/dist/commands/chat.d.ts +31 -45
  3. package/dist/commands/chat.d.ts.map +1 -1
  4. package/dist/commands/chat.js +374 -855
  5. package/dist/commands/chat.js.map +1 -1
  6. package/dist/commands/repo.d.ts +10 -0
  7. package/dist/commands/repo.d.ts.map +1 -1
  8. package/dist/commands/repo.js +215 -97
  9. package/dist/commands/repo.js.map +1 -1
  10. package/dist/index.js +32 -4
  11. package/dist/index.js.map +1 -1
  12. package/dist/utils/api.d.ts +8 -0
  13. package/dist/utils/api.d.ts.map +1 -1
  14. package/dist/utils/api.js +183 -42
  15. package/dist/utils/api.js.map +1 -1
  16. package/dist/utils/config.d.ts.map +1 -1
  17. package/dist/utils/config.js +2 -1
  18. package/dist/utils/config.js.map +1 -1
  19. package/dist/utils/tools.d.ts +3 -0
  20. package/dist/utils/tools.d.ts.map +1 -1
  21. package/dist/utils/tools.js +252 -14
  22. package/dist/utils/tools.js.map +1 -1
  23. package/package.json +13 -2
  24. package/install.ps1 +0 -290
  25. package/install.sh +0 -307
  26. package/src/commands/auth.ts +0 -226
  27. package/src/commands/chat.ts +0 -1101
  28. package/src/commands/config.ts +0 -306
  29. package/src/commands/deploy.ts +0 -609
  30. package/src/commands/edit.ts +0 -310
  31. package/src/commands/explain.ts +0 -115
  32. package/src/commands/generate.ts +0 -222
  33. package/src/commands/hub.ts +0 -382
  34. package/src/commands/repo.ts +0 -742
  35. package/src/commands/review.ts +0 -186
  36. package/src/index.ts +0 -601
  37. package/src/types/marked-terminal.d.ts +0 -31
  38. package/src/utils/api.ts +0 -526
  39. package/src/utils/config.ts +0 -241
  40. package/src/utils/files.ts +0 -273
  41. package/src/utils/logger.ts +0 -130
  42. package/src/utils/session.ts +0 -179
  43. package/src/utils/tools.ts +0 -1964
  44. package/test-parse.js +0 -105
  45. package/test-parse2.js +0 -35
  46. package/tsconfig.json +0 -20
@@ -1,1101 +0,0 @@
1
- /**
2
- * Interactive Chat Command for Vigthoria CLI
3
- *
4
- * Now with Vigthoria Autonomous agentic capabilities!
5
- */
6
-
7
- import chalk from 'chalk';
8
- import ora from 'ora';
9
- import * as readline from 'readline';
10
- import { Marked } from 'marked';
11
- import { markedTerminal } from 'marked-terminal';
12
- import { Config } from '../utils/config.js';
13
- import { Logger } from '../utils/logger.js';
14
- import { APIClient, ChatMessage } from '../utils/api.js';
15
- import { FileUtils } from '../utils/files.js';
16
- import { AgenticTools, ToolCall, ToolResult } from '../utils/tools.js';
17
- import { SessionManager, Session } from '../utils/session.js';
18
-
19
- interface ChatOptions {
20
- model: string;
21
- project: string;
22
- agent?: boolean;
23
- autoApprove?: boolean;
24
- resume?: boolean;
25
- stream?: boolean;
26
- }
27
-
28
- export class ChatCommand {
29
- private config: Config;
30
- private logger: Logger;
31
- private api: APIClient;
32
- private messages: ChatMessage[] = [];
33
- private fileUtils: FileUtils;
34
- private marked: Marked;
35
- private tools: AgenticTools | null = null;
36
- private agentMode: boolean = false;
37
- private rl: readline.Interface | null = null;
38
- private sessionManager: SessionManager;
39
- private currentSession: Session | null = null;
40
- private streamMode: boolean = true;
41
- private localMode: boolean = false;
42
-
43
- constructor(config: Config, logger: Logger) {
44
- this.config = config;
45
- this.logger = logger;
46
- this.api = new APIClient(config, logger);
47
- // Initialize with a safe default - will be updated in run()
48
- this.fileUtils = new FileUtils('.', config.get('project').ignorePatterns);
49
- this.sessionManager = new SessionManager();
50
-
51
- // Setup marked for terminal rendering
52
- this.marked = new Marked();
53
- this.marked.use(markedTerminal() as any);
54
- }
55
-
56
- async run(options: ChatOptions): Promise<void> {
57
- // Resolve and validate project path
58
- const projectPath = this.resolveProjectPath(options.project);
59
- if (!projectPath) {
60
- this.logger.error(`Project path not found: ${options.project}`);
61
- this.logger.info('Please specify a valid project directory with -p <path>');
62
- return;
63
- }
64
-
65
- // SECURITY: Warn if using home directory as workspace
66
- const homeDir = process.env.HOME || process.env.USERPROFILE || '';
67
- const isHomeDir = projectPath === homeDir || projectPath === homeDir.replace(/\\/g, '/');
68
- if (isHomeDir && options.agent) {
69
- console.log('');
70
- console.log(chalk.yellow('╔════════════════════════════════════════════════════════════╗'));
71
- console.log(chalk.yellow('║') + chalk.yellow.bold(' ⚠ WARNING: Agent mode in home directory ') + chalk.yellow('║'));
72
- console.log(chalk.yellow('╠════════════════════════════════════════════════════════════╣'));
73
- console.log(chalk.yellow('║') + ' You are running agent mode in your home directory. ' + chalk.yellow('║'));
74
- console.log(chalk.yellow('║') + ' The AI can access ALL files in this location. ' + chalk.yellow('║'));
75
- console.log(chalk.yellow('║') + ' ' + chalk.yellow('║'));
76
- console.log(chalk.yellow('║') + chalk.white(' For safety, navigate to your project folder first: ') + chalk.yellow('║'));
77
- console.log(chalk.yellow('║') + chalk.cyan(' cd C:\\path\\to\\your\\project ') + chalk.yellow('║'));
78
- console.log(chalk.yellow('║') + chalk.cyan(' vigthoria agent ') + chalk.yellow('║'));
79
- console.log(chalk.yellow('║') + ' ' + chalk.yellow('║'));
80
- console.log(chalk.yellow('║') + chalk.white(' Or specify a project path: ') + chalk.yellow('║'));
81
- console.log(chalk.yellow('║') + chalk.cyan(' vigthoria agent -p C:\\path\\to\\your\\project ') + chalk.yellow('║'));
82
- console.log(chalk.yellow('╚════════════════════════════════════════════════════════════╝'));
83
- console.log('');
84
-
85
- // Ask for confirmation
86
- const readline = require('readline');
87
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
88
- const answer = await new Promise<string>((resolve) => {
89
- rl.question(chalk.yellow('Continue anyway? [y/N] '), resolve);
90
- });
91
- rl.close();
92
-
93
- if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
94
- this.logger.info('Agent mode cancelled. Navigate to a project folder first.');
95
- return;
96
- }
97
- }
98
-
99
- // Update fileUtils with correct project path
100
- this.fileUtils = new FileUtils(projectPath, this.config.get('project').ignorePatterns);
101
-
102
- // STRICT AUTHENTICATION - No local bypass
103
- if (!this.config.isAuthenticated()) {
104
- this.logger.error('');
105
- this.logger.error('╔════════════════════════════════════════════════════════════╗');
106
- this.logger.error('║ Authentication Required ║');
107
- this.logger.error('╠════════════════════════════════════════════════════════════╣');
108
- this.logger.error('║ Vigthoria CLI requires a valid account and subscription. ║');
109
- this.logger.error('║ ║');
110
- this.logger.error('║ To get started: ║');
111
- this.logger.error('║ 1. Create account: https://coder.vigthoria.io/signup ║');
112
- this.logger.error('║ 2. Choose a plan: https://coder.vigthoria.io/pricing ║');
113
- this.logger.error('║ 3. Login: vigthoria login ║');
114
- this.logger.error('╚════════════════════════════════════════════════════════════╝');
115
- this.logger.error('');
116
- return;
117
- }
118
-
119
- // SUBSCRIPTION VALIDATION - Check plan is active
120
- if (!this.config.hasValidSubscription()) {
121
- // Revalidate subscription from server
122
- await this.api.getSubscriptionStatus();
123
-
124
- if (!this.config.hasValidSubscription()) {
125
- this.logger.error('');
126
- this.logger.error('╔════════════════════════════════════════════════════════════╗');
127
- this.logger.error('║ Subscription Required ║');
128
- this.logger.error('╠════════════════════════════════════════════════════════════╣');
129
- this.logger.error('║ Your subscription has expired or is inactive. ║');
130
- this.logger.error('║ ║');
131
- this.logger.error('║ Renew at: https://coder.vigthoria.io/pricing ║');
132
- this.logger.error('║ ║');
133
- this.logger.error('║ Plans start at $9/month for unlimited AI coding. ║');
134
- this.logger.error('╚════════════════════════════════════════════════════════════╝');
135
- this.logger.error('');
136
- return;
137
- }
138
- }
139
-
140
- // Setup agentic tools if enabled - use resolved project path
141
- this.agentMode = options.agent || false;
142
- this.streamMode = options.stream !== false; // Default to true
143
- this.localMode = false; // Never use local mode - always use Vigthoria API
144
-
145
- // CRITICAL: Agent mode REQUIRES stronger models to prevent hallucinations
146
- // Use Vigthoria Cloud for complex agent tasks
147
- if (this.agentMode) {
148
- // Upgrade model for agent mode if using a weak model
149
- const weakModels = ['fast', 'mini', 'vigthoria-fast-1.7b'];
150
- if (weakModels.includes(options.model)) {
151
- this.logger.warn(`Agent mode works better with stronger models. Upgrading from '${options.model}' to 'code'`);
152
- options.model = 'code';
153
- }
154
-
155
- this.tools = new AgenticTools(
156
- this.logger,
157
- projectPath, // Use resolved path
158
- this.askPermission.bind(this),
159
- options.autoApprove || false
160
- );
161
- }
162
-
163
- // Try to resume previous session or create new one
164
- if (options.resume) {
165
- this.currentSession = this.sessionManager.getLatest(projectPath);
166
- if (this.currentSession) {
167
- this.messages = [...this.currentSession.messages];
168
- this.logger.success(`Resumed session: ${this.currentSession.id}`);
169
- }
170
- }
171
-
172
- if (!this.currentSession) {
173
- this.currentSession = this.sessionManager.create(
174
- projectPath, // Use resolved path
175
- options.model,
176
- this.agentMode
177
- );
178
- }
179
-
180
- // Get project context with error handling
181
- let projectContext = { type: 'unknown', files: [] as string[], dependencies: {} as Record<string, string> };
182
- try {
183
- projectContext = await this.fileUtils.getProjectContext();
184
- } catch (error) {
185
- // Continue without project context if it fails
186
- this.logger.warn('Could not read project context, continuing without it.');
187
- }
188
-
189
- // Setup system message
190
- const systemMessage: ChatMessage = {
191
- role: 'system',
192
- content: this.buildSystemPrompt(projectContext, { ...options, project: projectPath }),
193
- };
194
- this.messages.push(systemMessage);
195
-
196
- this.printWelcome({ ...options, project: projectPath });
197
-
198
- // Start REPL
199
- await this.startRepl({ ...options, project: projectPath });
200
- }
201
-
202
- private buildSystemPrompt(
203
- projectContext: { type: string; files: string[]; dependencies: Record<string, string> },
204
- options: ChatOptions
205
- ): string {
206
- let prompt = `You are Vigthoria, the premier AI coding assistant of Vigthoria Technologies.
207
-
208
- ## IDENTITY & BRAND
209
- - Created by Vigthoria Technologies (NOT OpenAI, NOT Anthropic, NOT Microsoft)
210
- - Mascot: Viggy the Blue Queen
211
- - Philosophy: "Innovation through Intelligence"
212
- - Always respond as "Vigthoria" - never claim to be another AI
213
-
214
- ## VIGTHORIA ECOSYSTEM (Important - Know This!)
215
- Vigthoria is a comprehensive tech platform with these key services:
216
- - **Vigthoria Coder** (coder.vigthoria.io): AI-powered coding IDE and CLI (this tool!)
217
- - **Vigthoria Community** (community.vigthoria.io): Code sharing platform, NOT GitHub. When user mentions "repo", assume Vigthoria Community unless they say "GitHub"
218
- - **Vigthoria GoA** (agent.vigthoria.io): Graph of Agents - autonomous AI orchestration
219
- - **Vigthoria Meet** (meet.vigthoria.io): Video conferencing platform
220
- - **Vigthoria Pay**: Payment processing service
221
- - **Vigthoria B2C Hub**: Business-to-consumer services
222
- - **Model Router** (port 4009): Routes AI requests to optimal models
223
-
224
- ## CRITICAL RULES - NEVER VIOLATE:
225
- 1. **NEVER HALLUCINATE** - If you don't know something, say so. Don't make up file contents, directories, or project structures.
226
- 2. **NEVER ASSUME FILE CONTENTS** - If asked about a file, you MUST use read_file tool first. Do NOT guess what's in a file.
227
- 3. **NEVER CONFUSE PLATFORMS** - Vigthoria Community is NOT GitHub. Windows paths are NOT Unix paths.
228
- 4. **ALWAYS USE TOOLS** - In agent mode, USE the tools to read files, not your imagination.
229
-
230
- ## Project Context
231
- - Type: ${projectContext.type}
232
- - Root: ${options.project}
233
- - Key files: ${projectContext.files.slice(0, 10).join(', ')}
234
- ${projectContext.type === 'node' ? `- Dependencies: ${Object.keys(projectContext.dependencies).slice(0, 15).join(', ')}` : ''}
235
-
236
- ## CODE QUALITY STANDARDS (CRITICAL):
237
- 1. ALWAYS produce complete, production-ready code - no placeholders
238
- 2. When creating UI/HTML/CSS:
239
- - Ensure proper color contrast (text readable against backgrounds)
240
- - Use CSS variables for theming
241
- - Include responsive design
242
- 3. Include proper error handling
243
- 4. Add meaningful comments for complex logic
244
- 5. Follow modern best practices (2024-2026 standards)
245
- 6. Use semantic HTML, accessible patterns
246
- 7. Show COMPLETE implementations
247
-
248
- ## Guidelines:
249
- - Provide working code first, explanations second
250
- - Be concise but thorough
251
- - Excellence is the standard - mediocrity is not acceptable
252
- - If you need to see file contents, USE read_file FIRST before responding
253
-
254
- ## VIGTHORIA CLI COMMANDS (Know these to help users):
255
-
256
- ### Core Commands:
257
- - \`vigthoria chat\` (or \`vig c\`) - Start interactive chat (this session!)
258
- - \`vigthoria agent\` (or \`vig a\`) - Autonomous agent mode (can read/write files, run commands)
259
- - \`vigthoria edit <file>\` (or \`vig e\`) - Edit a file with AI assistance
260
- - \`vigthoria generate "desc"\` (or \`vig g\`) - Generate code from description
261
- - \`vigthoria explain <file>\` (or \`vig x\`) - Explain code in detail
262
- - \`vigthoria fix <file>\` (or \`vig f\`) - Fix bugs and issues
263
- - \`vigthoria review <file>\` (or \`vig r\`) - Code quality review
264
-
265
- ### Repository Commands (Vigthoria Community - NOT GitHub):
266
- - \`vigthoria repo push\` - Push project to Vigthoria Community repo
267
- - \`vigthoria repo pull <name>\` - Pull project from Vigthoria repo
268
- - \`vigthoria repo list\` - List your projects in Vigthoria repo
269
- - \`vigthoria repo status\` - Show sync status
270
- - \`vigthoria repo share <name>\` - Generate shareable link
271
- - \`vigthoria repo clone <url>\` - Clone a public project
272
- - \`vigthoria repo delete <name>\` - Remove from repo
273
-
274
- ### Hub/Marketplace Commands (Vigthoria API Modules):
275
- - \`vigthoria hub discover\` - Interactive module discovery
276
- - \`vigthoria hub list\` - List all API modules
277
- - \`vigthoria hub search <query>\` - Semantic search for modules
278
- - \`vigthoria hub activate <module>\` - Enable pay-as-you-go for module
279
- - \`vigthoria hub active\` - Show your active modules
280
- - \`vigthoria hub info <module>\` - Get module details
281
-
282
- ### Deployment Commands (Vigthoria Hosting):
283
- - \`vigthoria deploy preview\` - Deploy to free preview URL
284
- - \`vigthoria deploy subdomain <name>\` - Deploy to yourname.vigthoria.io
285
- - \`vigthoria deploy custom <domain>\` - Deploy to custom domain
286
- - \`vigthoria deploy list\` - List your deployments
287
- - \`vigthoria deploy plans\` - Show hosting plans/pricing
288
- - \`vigthoria deploy status\` - Check deployment status
289
- - \`vigthoria deploy verify <domain>\` - Verify DNS config
290
- - \`vigthoria deploy remove <domain>\` - Remove deployment
291
-
292
- ### Account & Config Commands:
293
- - \`vigthoria login\` - Authenticate with Vigthoria
294
- - \`vigthoria logout\` - Logout from account
295
- - \`vigthoria status\` - Show auth & subscription status
296
- - \`vigthoria config\` - Configure CLI settings
297
- - \`vigthoria update\` - Check for updates & upgrade CLI
298
- - \`vigthoria init\` - Initialize Vigthoria in project
299
-
300
- ## NATURAL LANGUAGE UNDERSTANDING (Interpret User Intent):
301
- When users say these phrases, understand their intent:
302
- - "push it", "upload this", "save to repo" → Run \`vigthoria repo push\`
303
- - "pull my project", "download from repo" → Run \`vigthoria repo pull\`
304
- - "deploy this", "host it", "put it online" → Run \`vigthoria deploy\`
305
- - "show my repos", "list projects" → Run \`vigthoria repo list\`
306
- - "share this project" → Run \`vigthoria repo share\`
307
- - "check for updates", "upgrade cli" → Run \`vigthoria update\`
308
- - "what APIs are available", "show modules" → Run \`vigthoria hub list\`
309
- - "enable music api", "activate <module>" → Run \`vigthoria hub activate\`
310
- - "my deployments", "where is it hosted" → Run \`vigthoria deploy list\`
311
- - "make it live", "publish" → Run \`vigthoria deploy\`
312
- - "fix bugs", "debug this" → Run \`vigthoria fix <file>\`
313
- - "explain this code" → Run \`vigthoria explain <file>\`
314
- - "review my code" → Run \`vigthoria review <file>\`
315
- - "create a component", "generate code for" → Run \`vigthoria generate\`
316
-
317
- ## Special Chat Commands (user may use these in chat):
318
- - /file <path> - Read and include a file in context
319
- - /edit <path> - Switch to file editing mode
320
- - /diff - Show pending changes
321
- - /apply - Apply pending changes
322
- - /clear - Clear conversation history
323
- - /model <name> - Switch AI model
324
- - /agent - Toggle agentic mode (Vigthoria autonomous actions)
325
- - /help - Show available commands`;
326
-
327
- // Add tool instructions if in agent mode
328
- if (this.agentMode && this.tools) {
329
- prompt += `\n\n${AgenticTools.getToolsForPrompt()}`;
330
- }
331
-
332
- return prompt;
333
- }
334
-
335
- /**
336
- * Resolve and validate project path - handles Windows paths properly
337
- */
338
- private resolveProjectPath(inputPath: string): string | null {
339
- const path = require('path');
340
- const fs = require('fs');
341
-
342
- try {
343
- // Resolve to absolute path
344
- let resolvedPath = inputPath;
345
-
346
- if (!path.isAbsolute(inputPath)) {
347
- resolvedPath = path.resolve(process.cwd(), inputPath);
348
- }
349
-
350
- // Normalize the path for the current OS
351
- resolvedPath = path.normalize(resolvedPath);
352
-
353
- // Check if path exists
354
- if (fs.existsSync(resolvedPath)) {
355
- const stats = fs.statSync(resolvedPath);
356
- if (stats.isDirectory()) {
357
- return resolvedPath;
358
- }
359
- }
360
-
361
- // Try current working directory as fallback
362
- const cwd = process.cwd();
363
- if (fs.existsSync(cwd)) {
364
- return cwd;
365
- }
366
-
367
- return null;
368
- } catch (error) {
369
- // Return current directory on any error
370
- try {
371
- return process.cwd();
372
- } catch {
373
- return null;
374
- }
375
- }
376
- }
377
-
378
- private printWelcome(options: ChatOptions): void {
379
- const sub = this.config.get('subscription');
380
-
381
- console.log();
382
- this.logger.box(
383
- `Model: ${chalk.cyan(options.model)}\n` +
384
- `Plan: ${chalk.green(sub.plan || 'free')}\n` +
385
- `Project: ${chalk.gray(options.project)}\n` +
386
- `Agent Mode: ${this.agentMode ? chalk.green('ON ✓') : chalk.gray('OFF')}`,
387
- 'Vigthoria Chat'
388
- );
389
- console.log();
390
- if (this.agentMode) {
391
- console.log(chalk.yellow('🤖 Agent Mode: AI can read files, edit code, and run commands autonomously.'));
392
- }
393
- console.log(chalk.gray('Type your message or /help for commands. Press Ctrl+C to exit.'));
394
- console.log();
395
- }
396
-
397
- private async startRepl(options: ChatOptions): Promise<void> {
398
- this.rl = readline.createInterface({
399
- input: process.stdin,
400
- output: process.stdout,
401
- prompt: chalk.cyan('you › '),
402
- terminal: true,
403
- });
404
-
405
- let currentModel = options.model;
406
- let pendingChanges: { file: string; content: string } | null = null;
407
- let isRunning = true;
408
-
409
- // Handle unexpected close events
410
- this.rl.on('close', () => {
411
- if (isRunning) {
412
- console.log(chalk.yellow('\n\n⚠ Session interrupted. Saving...'));
413
- if (this.currentSession && this.messages.length > 1) {
414
- this.sessionManager.save(this.currentSession);
415
- console.log(chalk.gray(`Session saved: ${this.currentSession.id}`));
416
- }
417
- console.log(chalk.cyan('Run `vigthoria chat --resume` to continue.\n'));
418
- isRunning = false;
419
- }
420
- });
421
-
422
- // Handle SIGINT (Ctrl+C) gracefully
423
- process.on('SIGINT', () => {
424
- if (isRunning) {
425
- isRunning = false;
426
- console.log(chalk.yellow('\n\nExiting...'));
427
- if (this.currentSession && this.messages.length > 1) {
428
- this.sessionManager.save(this.currentSession);
429
- console.log(chalk.gray(`Session saved: ${this.currentSession.id}`));
430
- }
431
- console.log(chalk.cyan('Goodbye! 👋\n'));
432
- process.exit(0);
433
- }
434
- });
435
-
436
- this.rl.prompt();
437
-
438
- for await (const line of this.rl) {
439
- if (!isRunning) break;
440
- const input = line.trim();
441
-
442
- if (!input) {
443
- this.rl.prompt();
444
- continue;
445
- }
446
-
447
- // Handle special commands
448
- if (input.startsWith('/')) {
449
- const [cmd, ...args] = input.slice(1).split(' ');
450
-
451
- switch (cmd) {
452
- case 'help':
453
- this.printHelp();
454
- break;
455
-
456
- case 'clear':
457
- this.messages = [this.messages[0]]; // Keep system message
458
- this.logger.success('Conversation cleared');
459
- break;
460
-
461
- case 'model':
462
- if (args[0]) {
463
- const newModel = args[0];
464
- currentModel = newModel;
465
-
466
- // Show branded model name
467
- const modelInfo = this.config.getAvailableModels().find(m => m.id === newModel);
468
- const isCloud = this.config.isCloudModel(newModel);
469
-
470
- if (isCloud) {
471
- console.log();
472
- console.log(chalk.magenta(' ☁️ Switched to: ') + chalk.magenta.bold(modelInfo?.name || 'Vigthoria Cloud'));
473
- console.log(chalk.gray(' 671B cloud model - ideal for complex tasks'));
474
- console.log();
475
- } else {
476
- console.log();
477
- console.log(chalk.green(' 🏠 Switched to: ') + chalk.green.bold(modelInfo?.name || newModel));
478
- console.log(chalk.gray(' Local model - fast, no API costs'));
479
- console.log();
480
- }
481
- } else {
482
- this.printModels();
483
- }
484
- break;
485
-
486
- case 'agent':
487
- this.agentMode = !this.agentMode;
488
- if (this.agentMode && !this.tools) {
489
- this.tools = new AgenticTools(
490
- this.logger,
491
- options.project,
492
- this.askPermission.bind(this),
493
- options.autoApprove || false
494
- );
495
- }
496
- // Rebuild system prompt with/without tools
497
- this.messages[0] = {
498
- role: 'system',
499
- content: this.buildSystemPrompt(
500
- await this.fileUtils.getProjectContext(),
501
- { ...options, agent: this.agentMode }
502
- ),
503
- };
504
- this.logger.success(`Agent mode: ${this.agentMode ? chalk.green('ON') : chalk.red('OFF')}`);
505
- if (this.agentMode) {
506
- console.log(chalk.yellow(' AI can now read files, edit code, and run commands.'));
507
- }
508
- break;
509
-
510
- case 'approve':
511
- if (this.tools) {
512
- options.autoApprove = !options.autoApprove;
513
- this.tools = new AgenticTools(
514
- this.logger,
515
- options.project,
516
- this.askPermission.bind(this),
517
- options.autoApprove
518
- );
519
- this.logger.success(`Auto-approve: ${options.autoApprove ? chalk.green('ON') : chalk.red('OFF')}`);
520
- if (options.autoApprove) {
521
- console.log(chalk.red(' ⚠️ AI actions will be executed without confirmation!'));
522
- }
523
- }
524
- break;
525
-
526
- case 'file':
527
- if (args[0]) {
528
- await this.addFileToContext(args[0]);
529
- } else {
530
- this.logger.error('Usage: /file <path>');
531
- }
532
- break;
533
-
534
- case 'edit':
535
- if (args[0]) {
536
- pendingChanges = await this.startEditMode(args[0], currentModel);
537
- } else {
538
- this.logger.error('Usage: /edit <path>');
539
- }
540
- break;
541
-
542
- case 'diff':
543
- if (pendingChanges) {
544
- this.showPendingDiff(pendingChanges);
545
- } else {
546
- this.logger.info('No pending changes');
547
- }
548
- break;
549
-
550
- case 'apply':
551
- if (pendingChanges) {
552
- this.applyChanges(pendingChanges);
553
- pendingChanges = null;
554
- } else {
555
- this.logger.info('No pending changes to apply');
556
- }
557
- break;
558
-
559
- case 'sessions':
560
- this.listSessions();
561
- break;
562
-
563
- case 'history':
564
- this.showHistory();
565
- break;
566
-
567
- case 'save':
568
- if (this.currentSession) {
569
- this.sessionManager.save(this.currentSession);
570
- this.logger.success(`Session saved: ${this.currentSession.id}`);
571
- }
572
- break;
573
-
574
- case 'new':
575
- // Start new session
576
- this.currentSession = this.sessionManager.create(
577
- options.project,
578
- currentModel,
579
- this.agentMode
580
- );
581
- this.messages = [this.messages[0]]; // Keep only system message
582
- this.logger.success(`New session: ${this.currentSession.id}`);
583
- break;
584
-
585
- case 'compact':
586
- // Compact context by summarizing older messages
587
- await this.compactContext(currentModel);
588
- break;
589
-
590
- case 'undo':
591
- // Undo last file operation
592
- if (this.tools) {
593
- const undoResult = await this.tools.undo();
594
- if (undoResult.success) {
595
- this.logger.success(undoResult.output || 'Undo completed');
596
- if (undoResult.metadata?.remainingUndos !== undefined) {
597
- console.log(chalk.gray(` ${undoResult.metadata.remainingUndos} more undo(s) available`));
598
- }
599
- } else {
600
- this.logger.error(undoResult.error || 'Nothing to undo');
601
- }
602
- } else {
603
- this.logger.info('Undo is only available in agent mode. Use /agent to enable.');
604
- }
605
- break;
606
-
607
- case 'logout':
608
- // Logout from Vigthoria
609
- this.config.clearAuth();
610
- this.logger.success('Logged out successfully');
611
- console.log(chalk.gray('Run `vigthoria login` to authenticate again.'));
612
- // Auto-save session before exit
613
- if (this.currentSession && this.messages.length > 1) {
614
- this.sessionManager.save(this.currentSession);
615
- }
616
- console.log(chalk.cyan('\nGoodbye! 👋\n'));
617
- this.rl!.close();
618
- return;
619
-
620
- case 'status':
621
- // Show current authentication status
622
- if (this.config.isAuthenticated()) {
623
- const email = this.config.get('email');
624
- const sub = this.config.get('subscription');
625
- console.log();
626
- console.log(chalk.green('✓ Logged in'));
627
- console.log(chalk.gray(' Email: ') + chalk.cyan(email));
628
- console.log(chalk.gray(' Plan: ') + chalk.green(sub.plan || 'free'));
629
- console.log(chalk.gray(' Model: ') + chalk.cyan(options.model));
630
- console.log(chalk.gray(' Agent: ') + (this.agentMode ? chalk.green('ON') : chalk.gray('OFF')));
631
- console.log();
632
- } else {
633
- console.log();
634
- this.logger.warn('Not logged in');
635
- console.log(chalk.gray('Run `vigthoria login` to authenticate'));
636
- console.log();
637
- }
638
- break;
639
-
640
- case 'exit':
641
- case 'quit':
642
- // Auto-save session on exit
643
- if (this.currentSession && this.messages.length > 1) {
644
- this.sessionManager.save(this.currentSession);
645
- console.log(chalk.gray(`Session saved: ${this.currentSession.id}`));
646
- }
647
- console.log(chalk.cyan('\nGoodbye! 👋\n'));
648
- this.rl!.close();
649
- return;
650
-
651
- default:
652
- this.logger.warn(`Unknown command: /${cmd}`);
653
- }
654
-
655
- this.rl!.prompt();
656
- continue;
657
- }
658
-
659
- // Regular chat message
660
- // Check if we should suggest Cloud upgrade for complex tasks
661
- this.suggestCloudUpgrade(currentModel, input);
662
-
663
- await this.chat(input, currentModel);
664
-
665
- // Save message to session
666
- if (this.currentSession) {
667
- this.currentSession.messages = [...this.messages];
668
- this.sessionManager.save(this.currentSession);
669
- }
670
-
671
- this.rl!.prompt();
672
- }
673
- }
674
-
675
- private async chat(userInput: string, model: string): Promise<void> {
676
- // Add user message (skip if empty - used for tool continuation)
677
- if (userInput) {
678
- this.messages.push({ role: 'user', content: userInput });
679
-
680
- // Clear session-approved tools for new user turn
681
- if (this.tools) {
682
- this.tools.clearSessionApprovals();
683
- }
684
- }
685
-
686
- const spinner = ora({
687
- text: chalk.gray('Thinking...'),
688
- spinner: 'dots',
689
- }).start();
690
-
691
- try {
692
- const response = await this.api.chat(this.messages, model, this.localMode);
693
-
694
- spinner.stop();
695
-
696
- // Add assistant message
697
- this.messages.push({ role: 'assistant', content: response.message });
698
-
699
- // Render markdown response
700
- console.log();
701
- console.log(chalk.cyan('vigthoria ›'));
702
- console.log(this.marked.parse(response.message));
703
-
704
- // Show token usage
705
- if (response.usage) {
706
- console.log(chalk.gray(`[${response.usage.total_tokens} tokens]`));
707
- }
708
- console.log();
709
-
710
- // In agent mode, check for and execute tool calls
711
- if (this.agentMode && this.tools) {
712
- const toolCalls = AgenticTools.parseToolCalls(response.message);
713
-
714
- if (toolCalls.length > 0) {
715
- await this.executeToolCalls(toolCalls, model);
716
- }
717
- }
718
-
719
- } catch (error) {
720
- spinner.stop();
721
- const errMsg = (error as Error).message || 'Unknown error';
722
-
723
- // Check for specific error types
724
- if (errMsg.includes('ECONNREFUSED') || errMsg.includes('ENOTFOUND')) {
725
- this.logger.error('Connection failed: Unable to reach AI service');
726
- console.log(chalk.gray(' Check your internet connection or try again later.'));
727
- } else if (errMsg.includes('timeout') || errMsg.includes('ETIMEDOUT')) {
728
- this.logger.error('Request timed out: AI service took too long to respond');
729
- console.log(chalk.gray(' Try a shorter query or check service status.'));
730
- } else if (errMsg.includes('401') || errMsg.includes('Unauthorized')) {
731
- this.logger.error('Authentication failed: Your session may have expired');
732
- console.log(chalk.gray(' Run `vigthoria login` to re-authenticate.'));
733
- } else {
734
- this.logger.error('Failed to get response:', errMsg);
735
- }
736
-
737
- // Remove failed user message
738
- this.messages.pop();
739
- }
740
- }
741
-
742
- /**
743
- * Execute tool calls from AI response (Vigthoria Autonomous)
744
- */
745
- private async executeToolCalls(calls: ToolCall[], model: string): Promise<void> {
746
- const results: { tool: string; result: ToolResult }[] = [];
747
-
748
- for (const call of calls) {
749
- console.log(chalk.yellow(`\n⚙ Executing: ${call.tool}`));
750
-
751
- const result = await this.tools!.execute(call);
752
- results.push({ tool: call.tool, result });
753
-
754
- if (result.success) {
755
- this.logger.success(`${call.tool}: Success`);
756
- if (result.output) {
757
- console.log(chalk.gray(this.truncateOutput(result.output)));
758
- }
759
- } else {
760
- this.logger.error(`${call.tool}: ${result.error}`);
761
- }
762
- }
763
-
764
- // Send tool results back to AI for continuation
765
- const toolResultsMessage: ChatMessage = {
766
- role: 'user',
767
- content: this.formatToolResults(results),
768
- };
769
- this.messages.push(toolResultsMessage);
770
-
771
- // Get AI's follow-up response
772
- console.log();
773
- await this.chat('', model);
774
- }
775
-
776
- /**
777
- * Format tool results for AI context
778
- */
779
- private formatToolResults(
780
- results: { tool: string; result: ToolResult }[]
781
- ): string {
782
- let msg = 'Tool execution results:\n\n';
783
-
784
- for (const { tool, result } of results) {
785
- msg += `## ${tool}\n`;
786
- msg += `Status: ${result.success ? 'Success' : 'Failed'}\n`;
787
- if (result.output) {
788
- msg += `Output:\n\`\`\`\n${this.truncateOutput(result.output)}\n\`\`\`\n`;
789
- }
790
- if (result.error) {
791
- msg += `Error: ${result.error}\n`;
792
- }
793
- msg += '\n';
794
- }
795
-
796
- msg += 'Continue with your analysis or next steps.';
797
- return msg;
798
- }
799
-
800
- /**
801
- * Truncate long output for context management
802
- */
803
- private truncateOutput(output: string, maxLines: number = 50): string {
804
- const lines = output.split('\n');
805
- if (lines.length <= maxLines) {
806
- return output;
807
- }
808
- return lines.slice(0, maxLines).join('\n') + `\n... (${lines.length - maxLines} more lines)`;
809
- }
810
-
811
- /**
812
- * Ask user for permission to execute dangerous action
813
- * Returns: true (yes), false (no), 'batch' (approve all of this type for this turn)
814
- */
815
- private async askPermission(action: string, options?: { batchApproval?: boolean }): Promise<boolean | 'batch'> {
816
- return new Promise((resolve) => {
817
- console.log('\n' + action);
818
-
819
- if (!this.rl) {
820
- resolve(false);
821
- return;
822
- }
823
-
824
- const prompt = options?.batchApproval
825
- ? chalk.yellow('Allow? [y/N/a] ')
826
- : chalk.yellow('Allow? [y/N] ');
827
-
828
- this.rl.question(prompt, (answer) => {
829
- const normalized = answer.toLowerCase().trim();
830
-
831
- // Check for batch approval first
832
- if (options?.batchApproval && (normalized === 'a' || normalized === 'all')) {
833
- resolve('batch');
834
- return;
835
- }
836
-
837
- const allowed = normalized === 'y' || normalized === 'yes';
838
- resolve(allowed);
839
- });
840
- });
841
- }
842
-
843
- private async addFileToContext(filePath: string): Promise<void> {
844
- const file = this.fileUtils.readFile(filePath);
845
-
846
- if (!file) {
847
- this.logger.error(`File not found: ${filePath}`);
848
- return;
849
- }
850
-
851
- // Add file content to messages
852
- const fileMessage: ChatMessage = {
853
- role: 'user',
854
- content: `Here is the content of ${file.relativePath} (${file.language}, ${file.lines} lines):\n\n\`\`\`${file.language}\n${file.content}\n\`\`\``,
855
- };
856
-
857
- this.messages.push(fileMessage);
858
- this.logger.success(`Added ${file.relativePath} to context (${file.lines} lines)`);
859
- }
860
-
861
- private async startEditMode(
862
- filePath: string,
863
- model: string
864
- ): Promise<{ file: string; content: string } | null> {
865
- const file = this.fileUtils.readFile(filePath);
866
-
867
- if (!file) {
868
- this.logger.error(`File not found: ${filePath}`);
869
- return null;
870
- }
871
-
872
- console.log();
873
- this.logger.section(`Editing: ${file.relativePath}`);
874
- console.log(chalk.gray('What changes would you like to make?'));
875
- console.log();
876
-
877
- // This would enter a sub-REPL for editing
878
- // For now, return the file info
879
- return { file: file.path, content: file.content };
880
- }
881
-
882
- private showPendingDiff(changes: { file: string; content: string }): void {
883
- const file = this.fileUtils.readFile(changes.file);
884
- if (!file) return;
885
-
886
- const diff = this.fileUtils.createDiff(file.content, changes.content);
887
-
888
- this.logger.section('Pending Changes');
889
- this.logger.diff(diff.added, diff.removed);
890
- }
891
-
892
- private applyChanges(changes: { file: string; content: string }): void {
893
- // Backup first
894
- const backup = this.fileUtils.backupFile(changes.file);
895
- if (backup) {
896
- this.logger.info(`Backup created: ${backup}`);
897
- }
898
-
899
- // Apply
900
- if (this.fileUtils.writeFile(changes.file, changes.content)) {
901
- this.logger.success(`Changes applied to ${changes.file}`);
902
- } else {
903
- this.logger.error('Failed to apply changes');
904
- }
905
- }
906
-
907
- private printHelp(): void {
908
- console.log();
909
- this.logger.section('Available Commands');
910
- console.log(chalk.yellow('── File & Context ──'));
911
- console.log(chalk.cyan('/file <path>') + ' - Add file to conversation context');
912
- console.log(chalk.cyan('/edit <path>') + ' - Start editing a file');
913
- console.log(chalk.cyan('/diff') + ' - Show pending changes');
914
- console.log(chalk.cyan('/apply') + ' - Apply pending changes');
915
- console.log();
916
- console.log(chalk.yellow('── AI & Models ──'));
917
- console.log(chalk.cyan('/model <name>') + ' - Switch AI model (or list available)');
918
- console.log(chalk.cyan('/agent') + ' - Toggle agentic mode (Vigthoria Autonomous)');
919
- console.log(chalk.cyan('/approve') + ' - Toggle auto-approve for agent actions');
920
- console.log(chalk.cyan('/undo') + ' - Undo last file operation (agent mode)');
921
- console.log();
922
- console.log(chalk.yellow('── Session & History ──'));
923
- console.log(chalk.cyan('/clear') + ' - Clear conversation history');
924
- console.log(chalk.cyan('/compact') + ' - Compact context (summarize older messages)');
925
- console.log(chalk.cyan('/sessions') + ' - List saved sessions');
926
- console.log(chalk.cyan('/history') + ' - Show conversation history');
927
- console.log(chalk.cyan('/save') + ' - Save current session');
928
- console.log(chalk.cyan('/new') + ' - Start new session');
929
- console.log();
930
- console.log(chalk.yellow('── Account ──'));
931
- console.log(chalk.cyan('/status') + ' - Show login & subscription status');
932
- console.log(chalk.cyan('/logout') + ' - Logout from Vigthoria');
933
- console.log();
934
- console.log(chalk.yellow('── General ──'));
935
- console.log(chalk.cyan('/help') + ' - Show this help');
936
- console.log(chalk.cyan('/exit') + ' - Exit Vigthoria (auto-saves)');
937
- console.log(chalk.cyan('/quit') + ' - Same as /exit');
938
- console.log();
939
- console.log(chalk.yellow('── CLI Commands (run in terminal) ──'));
940
- console.log(chalk.gray(' vigthoria repo push - Push to Vigthoria Community'));
941
- console.log(chalk.gray(' vigthoria repo pull - Pull from your repos'));
942
- console.log(chalk.gray(' vigthoria deploy - Deploy & host project'));
943
- console.log(chalk.gray(' vigthoria hub - Browse API modules'));
944
- console.log(chalk.gray(' vigthoria update - Update CLI to latest'));
945
- console.log();
946
- console.log(chalk.yellow('💡 Natural Language Tips:'));
947
- console.log(chalk.gray(' Say "push it" → I\'ll help with vigthoria repo push'));
948
- console.log(chalk.gray(' Say "deploy this" → I\'ll guide you through hosting'));
949
- console.log(chalk.gray(' Say "what APIs are available" → I\'ll show hub modules'));
950
- console.log();
951
-
952
- if (this.agentMode) {
953
- console.log(chalk.yellow('Agent Mode Tools:'));
954
- console.log(chalk.gray(' read_file, write_file, edit_file, bash, grep, list_dir, glob, git'));
955
- console.log();
956
- }
957
- }
958
-
959
- private listSessions(): void {
960
- const sessions = this.sessionManager.list();
961
-
962
- console.log();
963
- this.logger.section('Saved Sessions');
964
-
965
- if (sessions.length === 0) {
966
- console.log(chalk.gray(' No saved sessions'));
967
- } else {
968
- sessions.slice(0, 10).forEach(s => {
969
- const current = this.currentSession?.id === s.id ? chalk.green(' (current)') : '';
970
- const agent = s.agentMode ? chalk.yellow(' [agent]') : '';
971
- console.log(chalk.cyan(s.id) + agent + current);
972
- console.log(chalk.gray(` ${s.project} - ${new Date(s.updatedAt).toLocaleString()}`));
973
- });
974
- }
975
- console.log();
976
- }
977
-
978
- private showHistory(): void {
979
- console.log();
980
- this.logger.section('Conversation History');
981
-
982
- const userMessages = this.messages.filter(m => m.role !== 'system');
983
-
984
- if (userMessages.length === 0) {
985
- console.log(chalk.gray(' No messages yet'));
986
- } else {
987
- userMessages.forEach((m, i) => {
988
- const role = m.role === 'user' ? chalk.cyan('you') : chalk.green('vigthoria');
989
- const preview = m.content.substring(0, 60).replace(/\n/g, ' ');
990
- console.log(`${i + 1}. ${role}: ${chalk.gray(preview)}...`);
991
- });
992
- }
993
- console.log();
994
- }
995
-
996
- private async compactContext(model: string): Promise<void> {
997
- // If we have too many messages, summarize older ones
998
- if (this.messages.length < 10) {
999
- this.logger.info('Context is already compact');
1000
- return;
1001
- }
1002
-
1003
- const spinner = ora({
1004
- text: chalk.gray('Compacting context...'),
1005
- spinner: 'dots',
1006
- }).start();
1007
-
1008
- try {
1009
- // Keep system message and last 4 messages
1010
- const systemMessage = this.messages[0];
1011
- const recentMessages = this.messages.slice(-4);
1012
- const olderMessages = this.messages.slice(1, -4);
1013
-
1014
- if (olderMessages.length === 0) {
1015
- spinner.stop();
1016
- this.logger.info('Nothing to compact');
1017
- return;
1018
- }
1019
-
1020
- // Ask AI to summarize older conversation
1021
- const summaryResponse = await this.api.chat([
1022
- { role: 'system', content: 'Summarize this conversation in a concise way, preserving key context and decisions.' },
1023
- ...olderMessages,
1024
- ], model, this.localMode);
1025
-
1026
- // Create compacted context
1027
- this.messages = [
1028
- systemMessage,
1029
- { role: 'system', content: `[Previous conversation summary]: ${summaryResponse.message}` },
1030
- ...recentMessages,
1031
- ];
1032
-
1033
- spinner.stop();
1034
- this.logger.success(`Compacted ${olderMessages.length} messages into summary`);
1035
- } catch (error) {
1036
- spinner.stop();
1037
- this.logger.error('Failed to compact context:', (error as Error).message);
1038
- }
1039
- }
1040
-
1041
- private printModels(): void {
1042
- const models = this.config.getAvailableModels();
1043
-
1044
- console.log();
1045
- this.logger.section('═══ VIGTHORIA MODELS ═══');
1046
- console.log();
1047
-
1048
- // Group by tier
1049
- const localModels = models.filter(m => m.tier === 'local');
1050
- const cloudModels = models.filter(m => m.tier === 'cloud');
1051
-
1052
- console.log(chalk.green.bold(' 🏠 VIGTHORIA LOCAL (Self-hosted, fast, no API cost)'));
1053
- console.log(chalk.gray(' ─────────────────────────────────────────────────'));
1054
- localModels.forEach(m => {
1055
- const isDefault = m.id === 'code';
1056
- const marker = isDefault ? chalk.yellow(' ★ DEFAULT') : '';
1057
- console.log(chalk.cyan(' ' + m.id.padEnd(15)) + chalk.white(m.name.padEnd(25)) + chalk.gray(m.description) + marker);
1058
- });
1059
-
1060
- if (cloudModels.length > 0) {
1061
- console.log();
1062
- console.log(chalk.magenta.bold(' ☁️ VIGTHORIA CLOUD (Premium, for complex tasks)'));
1063
- console.log(chalk.gray(' ─────────────────────────────────────────────────'));
1064
- cloudModels.forEach(m => {
1065
- console.log(chalk.cyan(' ' + m.id.padEnd(15)) + chalk.white(m.name.padEnd(25)) + chalk.gray(m.description));
1066
- });
1067
- console.log();
1068
- console.log(chalk.yellow(' 💡 Tip: Use /model cloud for complex multi-file tasks'));
1069
- } else {
1070
- console.log();
1071
- console.log(chalk.yellow(' 💡 Upgrade to Pro for Vigthoria Cloud (671B models)'));
1072
- }
1073
- console.log();
1074
- }
1075
-
1076
- // Suggest Cloud upgrade for complex tasks
1077
- private suggestCloudUpgrade(currentModel: string, prompt: string): boolean {
1078
- // Don't suggest if already on cloud
1079
- if (this.config.isCloudModel(currentModel)) {
1080
- return false;
1081
- }
1082
-
1083
- // Check if task seems complex
1084
- if (this.config.isComplexTask(prompt)) {
1085
- console.log();
1086
- console.log(chalk.yellow('╔══════════════════════════════════════════════════════════╗'));
1087
- console.log(chalk.yellow('║') + chalk.white.bold(' 💡 This looks like a complex task! ') + chalk.yellow('║'));
1088
- console.log(chalk.yellow('║') + chalk.gray(' Current: ') + chalk.cyan(currentModel.padEnd(43)) + chalk.yellow('║'));
1089
- console.log(chalk.yellow('║') + chalk.gray(' Suggested: ') + chalk.magenta('Vigthoria Cloud (671B)'.padEnd(41)) + chalk.yellow('║'));
1090
- console.log(chalk.yellow('║ ║'));
1091
- console.log(chalk.yellow('║') + chalk.white(' Type ') + chalk.cyan('/model cloud') + chalk.white(' for better results on: ') + chalk.yellow('║'));
1092
- console.log(chalk.yellow('║') + chalk.gray(' • Multi-file refactoring ') + chalk.yellow('║'));
1093
- console.log(chalk.yellow('║') + chalk.gray(' • Architecture decisions ') + chalk.yellow('║'));
1094
- console.log(chalk.yellow('║') + chalk.gray(' • Complex feature implementation ') + chalk.yellow('║'));
1095
- console.log(chalk.yellow('╚══════════════════════════════════════════════════════════╝'));
1096
- console.log();
1097
- return true;
1098
- }
1099
- return false;
1100
- }
1101
- }