vigthoria-cli 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -51,7 +51,7 @@ function getVersion(): string {
51
51
  } catch (e) {
52
52
  // Fallback to hardcoded version
53
53
  }
54
- return '1.1.0';
54
+ return '1.4.0';
55
55
  }
56
56
  const VERSION = getVersion();
57
57
 
@@ -83,8 +83,16 @@ async function checkForUpdatesQuietly(): Promise<void> {
83
83
 
84
84
  // Only show update message if npm version is NEWER than current (not older)
85
85
  if (npmVersion && compareVersions(npmVersion, VERSION) > 0) {
86
- console.log(chalk.yellow(`\nšŸ“¦ Update available: ${VERSION} → ${npmVersion}`));
87
- console.log(chalk.gray(' Run `vigthoria update` to install\n'));
86
+ // Check if this is a security update (1.4.0+)
87
+ if (compareVersions(npmVersion, '1.4.0') >= 0 && compareVersions(VERSION, '1.4.0') < 0) {
88
+ console.log(chalk.red.bold('\nāš ļø SECURITY UPDATE AVAILABLE'));
89
+ console.log(chalk.red(` Version ${VERSION} has security vulnerabilities.`));
90
+ console.log(chalk.yellow(` Please update to ${npmVersion} immediately:`));
91
+ console.log(chalk.white.bold(' npm install -g vigthoria-cli@latest\n'));
92
+ } else {
93
+ console.log(chalk.yellow(`\nšŸ“¦ Update available: ${VERSION} → ${npmVersion}`));
94
+ console.log(chalk.gray(' Run `vigthoria update` to install\n'));
95
+ }
88
96
  }
89
97
  } catch {
90
98
  // Silently ignore - network issues shouldn't block CLI
@@ -143,7 +151,7 @@ async function main() {
143
151
  });
144
152
  });
145
153
 
146
- // Agent command - Agentic mode (Claude Code-like)
154
+ // Agent command - Agentic mode (Vigthoria Autonomous)
147
155
  program
148
156
  .command('agent')
149
157
  .alias('a')
@@ -178,9 +186,10 @@ async function main() {
178
186
  .command('generate <description>')
179
187
  .alias('g')
180
188
  .description('Generate code from description')
181
- .option('-l, --language <lang>', 'Target language', 'typescript')
189
+ .option('-l, --language <lang>', 'Target language (html, typescript, python, etc.)', 'html')
182
190
  .option('-o, --output <file>', 'Output file path')
183
191
  .option('-m, --model <model>', 'Select AI model', 'code')
192
+ .option('-p, --pro', 'Senior Developer Mode: plan, generate, quality check (recommended)', false)
184
193
  .action(async (description, options) => {
185
194
  const generate = new GenerateCommand(config, logger);
186
195
  await generate.run(description, options);
package/src/utils/api.ts CHANGED
@@ -252,7 +252,29 @@ export class APIClient {
252
252
  }
253
253
  }
254
254
 
255
- // Strategy 1: Try local Model Router's Vigthoria chat endpoint
255
+ // Strategy 1: Try Vigthoria Inference Server directly (NATIVE models on port 8010)
256
+ try {
257
+ const response = await axios.post('http://localhost:8010/v1/chat/completions', {
258
+ model: resolvedModel,
259
+ messages,
260
+ max_tokens: this.config.get('preferences').maxTokens,
261
+ temperature: 0.7,
262
+ stream: false,
263
+ }, { timeout: 120000 });
264
+
265
+ if (response.data.choices && response.data.choices.length > 0) {
266
+ return {
267
+ id: response.data.id || `vigthoria-native-${Date.now()}`,
268
+ message: response.data.choices[0].message?.content || response.data.choices[0].text,
269
+ model: response.data.model || model,
270
+ usage: response.data.usage,
271
+ };
272
+ }
273
+ } catch (error) {
274
+ this.logger.debug('Vigthoria Inference Server (8010) failed, trying Model Router...');
275
+ }
276
+
277
+ // Strategy 2: Try local Model Router's Vigthoria chat endpoint
256
278
  try {
257
279
  const response = await axios.post('http://localhost:4009/api/vigthoria/chat', {
258
280
  messages,
@@ -271,10 +293,10 @@ export class APIClient {
271
293
  };
272
294
  }
273
295
  } catch (error) {
274
- this.logger.debug('Model router failed, trying Ollama directly...');
296
+ this.logger.debug('Model Router (4009) failed, trying Ollama directly...');
275
297
  }
276
298
 
277
- // Strategy 2: Try Ollama directly (for local development/testing)
299
+ // Strategy 3: Try Ollama directly (for local development/testing)
278
300
  try {
279
301
  const ollamaModel = this.resolveToOllamaModel(model);
280
302
  const prompt = this.formatMessagesForOllama(messages);
@@ -302,17 +324,20 @@ export class APIClient {
302
324
  throw new Error('AI service unavailable. Please check your authentication with `vigthoria login` or try again later.');
303
325
  }
304
326
 
305
- // Map CLI model names to Ollama model names (for local fallback)
327
+ // Map CLI model names to Ollama model names (for local/offline fallback)
328
+ // Vigthoria_v3_Code_30B runs on qwen3-coder base via Vigthoria Cloud
329
+ // Local users with Ollama can use the base model for offline work
306
330
  private resolveToOllamaModel(model: string): string {
307
331
  const ollamaMap: Record<string, string> = {
308
332
  'fast': 'qwen3:0.6b',
309
333
  'mini': 'smollm2:135m',
310
- 'code': 'qwen2.5-coder:7b',
334
+ 'code': 'qwen3-coder:latest', // Vigthoria_v3_Code_30B (cloud) / qwen3-coder (local fallback)
311
335
  'balanced': 'phi3:mini',
312
336
  'creative': 'gemma3:latest',
313
337
  'vigthoria-fast-1.7b': 'qwen3:0.6b',
314
338
  'vigthoria-mini-0.6b': 'smollm2:135m',
315
- 'vigthoria-v2-code-8b': 'qwen2.5-coder:7b',
339
+ 'vigthoria-v3-code-30b': 'qwen3-coder:latest', // Vigthoria_v3_Code_30B
340
+ 'vigthoria-v2-code-8b': 'qwen3-coder:latest', // Legacy v2
316
341
  'vigthoria-balanced-4b': 'phi3:mini',
317
342
  'vigthoria-creative-9b-v4': 'gemma3:latest',
318
343
  };
@@ -436,6 +461,37 @@ export class APIClient {
436
461
  return response.data.code;
437
462
  }
438
463
 
464
+ // Senior Developer Mode - Planning + Generation + Quality Check
465
+ async generateProject(prompt: string, projectType: string, model: string): Promise<{
466
+ code: string;
467
+ plan?: any;
468
+ quality?: {
469
+ lineCount: number;
470
+ score: number;
471
+ checks: {
472
+ hasAnimations: boolean;
473
+ hasNeonEffects: boolean;
474
+ hasResponsive: boolean;
475
+ hasFontAwesome: boolean;
476
+ hasGoogleFonts: boolean;
477
+ };
478
+ };
479
+ }> {
480
+ const response = await this.client.post('/api/ai/generate-project', {
481
+ prompt,
482
+ projectType,
483
+ model: this.resolveModelId(model),
484
+ }, {
485
+ timeout: 300000, // 5 minutes for complex generation
486
+ });
487
+
488
+ return {
489
+ code: response.data.code,
490
+ plan: response.data.plan,
491
+ quality: response.data.quality,
492
+ };
493
+ }
494
+
439
495
  async explainCode(code: string, language: string): Promise<string> {
440
496
  const response = await this.client.post('/api/ai/explain', {
441
497
  code,
@@ -538,10 +594,17 @@ export class APIClient {
538
594
  // Health check
539
595
  async healthCheck(): Promise<boolean> {
540
596
  try {
541
- await this.client.get('/health');
542
- return true;
597
+ // Try the main API health endpoint first
598
+ const response = await this.client.get('/api/health');
599
+ return response.data?.status === 'ok' || response.data?.healthy === true;
543
600
  } catch {
544
- return false;
601
+ // Fallback: try root health endpoint
602
+ try {
603
+ await this.client.get('/health');
604
+ return true;
605
+ } catch {
606
+ return false;
607
+ }
545
608
  }
546
609
  }
547
610
  }
@@ -206,7 +206,7 @@ export class Config {
206
206
  // Pro plans get code and creative models
207
207
  if (sub.plan === 'developer' || sub.plan === 'pro' || sub.plan === 'ultra' || sub.plan === 'enterprise') {
208
208
  models.push(
209
- { id: 'code', name: 'Vigthoria Code v2 8B', description: 'Code specialist (training)' },
209
+ { id: 'code', name: 'Vigthoria_v3_Code_30B', description: 'Advanced code specialist with agent mode' },
210
210
  { id: 'creative', name: 'Vigthoria Creative 9B v4', description: 'Creative writing, lyrics' },
211
211
  );
212
212
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Session Manager - Persist and resume conversations
3
- * Similar to Claude Code's session persistence
3
+ * Similar to Vigthoria's session persistence
4
4
  */
5
5
 
6
6
  import * as fs from 'fs';
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Vigthoria CLI Tools - Agentic Tool System
3
3
  *
4
- * This module provides Claude Code-like autonomous tool execution.
4
+ * This module provides Vigthoria Autonomous autonomous tool execution.
5
5
  * Tools can be called by the AI to perform actions.
6
6
  *
7
7
  * Enhanced with:
@@ -159,7 +159,7 @@ export class AgenticTools {
159
159
  }
160
160
 
161
161
  /**
162
- * List of available tools - similar to Claude Code
162
+ * List of available tools - Vigthoria's advanced
163
163
  * Enhanced with risk levels and categories
164
164
  */
165
165
  static getToolDefinitions(): ToolDefinition[] {
@@ -726,11 +726,40 @@ export class AgenticTools {
726
726
 
727
727
  /**
728
728
  * Execute bash command with enhanced error handling
729
+ * SECURITY: Commands are sandboxed to workspace directory
729
730
  */
730
731
  private bash(args: Record<string, string>): ToolResult {
731
732
  const cwd = args.cwd ? this.resolvePath(args.cwd) : this.cwd;
732
733
  const timeout = args.timeout ? parseInt(args.timeout) * 1000 : 30000;
733
734
 
735
+ // SECURITY: Block dangerous commands that could access outside workspace
736
+ const blockedPatterns = [
737
+ /\bcat\s+\/etc\//i, // Reading system files
738
+ /\bcat\s+\/var\/(?!log)/i, // Reading /var/ except logs
739
+ /\bcat\s+\/root\//i, // Reading root home
740
+ /\bcat\s+\/home\/[^\/]+\/\.[^\/]/i, // Reading hidden files in home dirs
741
+ /\bcat\s+~\/\./i, // Reading hidden files via ~
742
+ /\bcd\s+(\/etc|\/var\/www|\/root|\/home)/i, // CD to sensitive dirs
743
+ /\brm\s+-rf?\s+\//i, // Dangerous rm commands
744
+ /\b(curl|wget).*\|\s*(bash|sh)\b/i, // Downloading and executing scripts
745
+ /\bsudo\b/i, // Sudo commands
746
+ /\bsu\s+-/i, // Su to root
747
+ /\bchmod\s+[0-7]*777/i, // World-writable permissions
748
+ /\/(var\/www|opt)\/[a-z]+-?(database|models|coder|mcp|operator|voice|music)/i, // Vigthoria ecosystem
749
+ /vigthoria-(models|database|mcp|operator|voice|music)/i, // Vigthoria project names
750
+ ];
751
+
752
+ for (const pattern of blockedPatterns) {
753
+ if (pattern.test(args.command)) {
754
+ this.logger.warn(`Security: Blocked potentially dangerous command: ${args.command.substring(0, 50)}...`);
755
+ return this.createErrorResult(
756
+ ToolErrorType.PERMISSION_DENIED,
757
+ 'This command is blocked for security reasons',
758
+ 'Commands must operate within your current project workspace.'
759
+ );
760
+ }
761
+ }
762
+
734
763
  // Validate working directory
735
764
  if (!fs.existsSync(cwd)) {
736
765
  return this.createErrorResult(
@@ -983,15 +1012,43 @@ export class AgenticTools {
983
1012
  }
984
1013
  }
985
1014
 
1015
+ /**
1016
+ * Resolve and SANITIZE path - prevent path traversal outside workspace
1017
+ * SECURITY: All paths MUST stay within the workspace (cwd)
1018
+ */
986
1019
  private resolvePath(p: string): string {
1020
+ // Resolve the full path (handles both relative and absolute)
1021
+ let resolvedPath: string;
987
1022
  if (path.isAbsolute(p)) {
988
- return p;
1023
+ resolvedPath = path.normalize(p);
1024
+ } else {
1025
+ resolvedPath = path.normalize(path.join(this.cwd, p));
1026
+ }
1027
+
1028
+ // SECURITY CHECK: Ensure path is within workspace
1029
+ const workspaceRoot = path.normalize(this.cwd);
1030
+ if (!resolvedPath.startsWith(workspaceRoot + path.sep) && resolvedPath !== workspaceRoot) {
1031
+ // Path is outside workspace - force it back to workspace
1032
+ this.logger.warn(`Security: Blocked access to path outside workspace: ${p}`);
1033
+ // Return the sanitized relative path within workspace
1034
+ const basename = path.basename(p);
1035
+ return path.join(this.cwd, basename);
989
1036
  }
990
- return path.join(this.cwd, p);
1037
+
1038
+ return resolvedPath;
1039
+ }
1040
+
1041
+ /**
1042
+ * Check if a path is within the allowed workspace
1043
+ */
1044
+ private isPathWithinWorkspace(p: string): boolean {
1045
+ const resolvedPath = path.normalize(path.isAbsolute(p) ? p : path.join(this.cwd, p));
1046
+ const workspaceRoot = path.normalize(this.cwd);
1047
+ return resolvedPath.startsWith(workspaceRoot + path.sep) || resolvedPath === workspaceRoot;
991
1048
  }
992
1049
 
993
1050
  /**
994
- * Parse tool calls from AI response (Claude Code format)
1051
+ * Parse tool calls from AI response (Vigthoria Agent format)
995
1052
  * Enhanced to handle various AI output formats including malformed JSON
996
1053
  */
997
1054
  static parseToolCalls(text: string): ToolCall[] {
@@ -1170,25 +1227,28 @@ To use a tool, output a JSON block in a code fence with "tool" language:
1170
1227
 
1171
1228
  1. List directory contents:
1172
1229
  \`\`\`tool
1173
- {"tool": "list_dir", "args": {"path": "/var/www/project"}}
1230
+ {"tool": "list_dir", "args": {"path": "."}}
1174
1231
  \`\`\`
1175
1232
 
1176
1233
  2. Read a file:
1177
1234
  \`\`\`tool
1178
- {"tool": "read_file", "args": {"path": "/var/www/project/index.html"}}
1235
+ {"tool": "read_file", "args": {"path": "src/index.js"}}
1179
1236
  \`\`\`
1180
1237
 
1181
1238
  3. Run a shell command:
1182
1239
  \`\`\`tool
1183
- {"tool": "bash", "args": {"command": "ls -la /var/www"}}
1240
+ {"tool": "bash", "args": {"command": "ls -la"}}
1184
1241
  \`\`\`
1185
1242
 
1186
1243
  4. Write a file:
1187
1244
  \`\`\`tool
1188
- {"tool": "write_file", "args": {"path": "/var/www/project/new.txt", "content": "Hello World"}}
1245
+ {"tool": "write_file", "args": {"path": "hello.py", "content": "print('Hello World')"}}
1189
1246
  \`\`\`
1190
1247
 
1191
1248
  IMPORTANT:
1249
+ - You can ONLY access files within the current project workspace
1250
+ - Use relative paths (e.g., "src/file.js", "app.py", "./config.json")
1251
+ - Never try to access system files or directories outside the workspace
1192
1252
  - Use ONLY the exact tool names: list_dir, read_file, write_file, edit_file, bash, grep, glob, git
1193
1253
  - The JSON must be valid with double quotes for all keys and string values
1194
1254
  - After tool execution, you will receive results and can continue with the next step