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.
- package/README.md +52 -1
- package/dist/commands/chat.d.ts +31 -45
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +374 -855
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/repo.d.ts +10 -0
- package/dist/commands/repo.d.ts.map +1 -1
- package/dist/commands/repo.js +215 -97
- package/dist/commands/repo.js.map +1 -1
- package/dist/index.js +32 -4
- package/dist/index.js.map +1 -1
- package/dist/utils/api.d.ts +8 -0
- package/dist/utils/api.d.ts.map +1 -1
- package/dist/utils/api.js +183 -42
- package/dist/utils/api.js.map +1 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +2 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/tools.d.ts +3 -0
- package/dist/utils/tools.d.ts.map +1 -1
- package/dist/utils/tools.js +252 -14
- package/dist/utils/tools.js.map +1 -1
- package/package.json +13 -2
- package/install.ps1 +0 -290
- package/install.sh +0 -307
- package/src/commands/auth.ts +0 -226
- package/src/commands/chat.ts +0 -1101
- package/src/commands/config.ts +0 -306
- package/src/commands/deploy.ts +0 -609
- package/src/commands/edit.ts +0 -310
- package/src/commands/explain.ts +0 -115
- package/src/commands/generate.ts +0 -222
- package/src/commands/hub.ts +0 -382
- package/src/commands/repo.ts +0 -742
- package/src/commands/review.ts +0 -186
- package/src/index.ts +0 -601
- package/src/types/marked-terminal.d.ts +0 -31
- package/src/utils/api.ts +0 -526
- package/src/utils/config.ts +0 -241
- package/src/utils/files.ts +0 -273
- package/src/utils/logger.ts +0 -130
- package/src/utils/session.ts +0 -179
- package/src/utils/tools.ts +0 -1964
- package/test-parse.js +0 -105
- package/test-parse2.js +0 -35
- package/tsconfig.json +0 -20
package/src/commands/chat.ts
DELETED
|
@@ -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
|
-
}
|