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