vigthoria-cli 1.4.1 → 1.4.3

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.
@@ -44,7 +44,8 @@ export class ChatCommand {
44
44
  this.config = config;
45
45
  this.logger = logger;
46
46
  this.api = new APIClient(config, logger);
47
- this.fileUtils = new FileUtils(process.cwd(), config.get('project').ignorePatterns);
47
+ // Initialize with a safe default - will be updated in run()
48
+ this.fileUtils = new FileUtils('.', config.get('project').ignorePatterns);
48
49
  this.sessionManager = new SessionManager();
49
50
 
50
51
  // Setup marked for terminal rendering
@@ -53,6 +54,17 @@ export class ChatCommand {
53
54
  }
54
55
 
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
+ // Update fileUtils with correct project path
66
+ this.fileUtils = new FileUtils(projectPath, this.config.get('project').ignorePatterns);
67
+
56
68
  // STRICT AUTHENTICATION - No local bypass
57
69
  if (!this.config.isAuthenticated()) {
58
70
  this.logger.error('');
@@ -91,7 +103,7 @@ export class ChatCommand {
91
103
  }
92
104
  }
93
105
 
94
- // Setup agentic tools if enabled
106
+ // Setup agentic tools if enabled - use resolved project path
95
107
  this.agentMode = options.agent || false;
96
108
  this.streamMode = options.stream !== false; // Default to true
97
109
  this.localMode = false; // Never use local mode - always use Vigthoria API
@@ -99,7 +111,7 @@ export class ChatCommand {
99
111
  if (this.agentMode) {
100
112
  this.tools = new AgenticTools(
101
113
  this.logger,
102
- options.project,
114
+ projectPath, // Use resolved path
103
115
  this.askPermission.bind(this),
104
116
  options.autoApprove || false
105
117
  );
@@ -107,7 +119,7 @@ export class ChatCommand {
107
119
 
108
120
  // Try to resume previous session or create new one
109
121
  if (options.resume) {
110
- this.currentSession = this.sessionManager.getLatest(options.project);
122
+ this.currentSession = this.sessionManager.getLatest(projectPath);
111
123
  if (this.currentSession) {
112
124
  this.messages = [...this.currentSession.messages];
113
125
  this.logger.success(`Resumed session: ${this.currentSession.id}`);
@@ -116,33 +128,44 @@ export class ChatCommand {
116
128
 
117
129
  if (!this.currentSession) {
118
130
  this.currentSession = this.sessionManager.create(
119
- options.project,
131
+ projectPath, // Use resolved path
120
132
  options.model,
121
133
  this.agentMode
122
134
  );
123
135
  }
124
136
 
125
- // Get project context
126
- const projectContext = await this.fileUtils.getProjectContext();
137
+ // Get project context with error handling
138
+ let projectContext = { type: 'unknown', files: [] as string[], dependencies: {} as Record<string, string> };
139
+ try {
140
+ projectContext = await this.fileUtils.getProjectContext();
141
+ } catch (error) {
142
+ // Continue without project context if it fails
143
+ this.logger.warn('Could not read project context, continuing without it.');
144
+ }
127
145
 
128
146
  // Setup system message
129
147
  const systemMessage: ChatMessage = {
130
148
  role: 'system',
131
- content: this.buildSystemPrompt(projectContext, options),
149
+ content: this.buildSystemPrompt(projectContext, { ...options, project: projectPath }),
132
150
  };
133
151
  this.messages.push(systemMessage);
134
152
 
135
- this.printWelcome(options);
153
+ this.printWelcome({ ...options, project: projectPath });
136
154
 
137
155
  // Start REPL
138
- await this.startRepl(options);
156
+ await this.startRepl({ ...options, project: projectPath });
139
157
  }
140
158
 
141
159
  private buildSystemPrompt(
142
160
  projectContext: { type: string; files: string[]; dependencies: Record<string, string> },
143
161
  options: ChatOptions
144
162
  ): string {
145
- let prompt = `You are Vigthoria, a premium AI coding assistant. You help developers write, understand, and improve code.
163
+ let prompt = `You are Vigthoria, the premier AI coding assistant of Vigthoria Technologies.
164
+
165
+ IDENTITY:
166
+ - Created by Vigthoria Technologies
167
+ - Mascot: Viggy the Blue Queen
168
+ - Philosophy: "Innovation through Intelligence"
146
169
 
147
170
  Project Context:
148
171
  - Type: ${projectContext.type}
@@ -150,12 +173,22 @@ Project Context:
150
173
  - Key files: ${projectContext.files.slice(0, 10).join(', ')}
151
174
  ${projectContext.type === 'node' ? `- Dependencies: ${Object.keys(projectContext.dependencies).slice(0, 15).join(', ')}` : ''}
152
175
 
176
+ CODE QUALITY STANDARDS (CRITICAL):
177
+ 1. ALWAYS produce complete, production-ready code - no placeholders
178
+ 2. When creating UI/HTML/CSS:
179
+ - Ensure proper color contrast (text readable against backgrounds)
180
+ - Use CSS variables for theming
181
+ - Include responsive design
182
+ 3. Include proper error handling
183
+ 4. Add meaningful comments for complex logic
184
+ 5. Follow modern best practices (2024-2026 standards)
185
+ 6. Use semantic HTML, accessible patterns
186
+ 7. Show COMPLETE implementations
187
+
153
188
  Guidelines:
154
- - Provide complete, production-ready code
155
- - Use modern best practices (2024-2026 standards)
156
- - Include proper error handling
157
- - Explain your reasoning when helpful
189
+ - Provide working code first, explanations second
158
190
  - Be concise but thorough
191
+ - Excellence is the standard - mediocrity is not acceptable
159
192
 
160
193
  Special Commands (user may use these):
161
194
  - /file <path> - Read and include a file in context
@@ -164,7 +197,7 @@ Special Commands (user may use these):
164
197
  - /apply - Apply pending changes
165
198
  - /clear - Clear conversation history
166
199
  - /model <name> - Switch AI model
167
- - /agent - Toggle agentic mode (Vigthoria Autonomous autonomous actions)
200
+ - /agent - Toggle agentic mode (Vigthoria autonomous actions)
168
201
  - /help - Show available commands`;
169
202
 
170
203
  // Add tool instructions if in agent mode
@@ -175,6 +208,49 @@ Special Commands (user may use these):
175
208
  return prompt;
176
209
  }
177
210
 
211
+ /**
212
+ * Resolve and validate project path - handles Windows paths properly
213
+ */
214
+ private resolveProjectPath(inputPath: string): string | null {
215
+ const path = require('path');
216
+ const fs = require('fs');
217
+
218
+ try {
219
+ // Resolve to absolute path
220
+ let resolvedPath = inputPath;
221
+
222
+ if (!path.isAbsolute(inputPath)) {
223
+ resolvedPath = path.resolve(process.cwd(), inputPath);
224
+ }
225
+
226
+ // Normalize the path for the current OS
227
+ resolvedPath = path.normalize(resolvedPath);
228
+
229
+ // Check if path exists
230
+ if (fs.existsSync(resolvedPath)) {
231
+ const stats = fs.statSync(resolvedPath);
232
+ if (stats.isDirectory()) {
233
+ return resolvedPath;
234
+ }
235
+ }
236
+
237
+ // Try current working directory as fallback
238
+ const cwd = process.cwd();
239
+ if (fs.existsSync(cwd)) {
240
+ return cwd;
241
+ }
242
+
243
+ return null;
244
+ } catch (error) {
245
+ // Return current directory on any error
246
+ try {
247
+ return process.cwd();
248
+ } catch {
249
+ return null;
250
+ }
251
+ }
252
+ }
253
+
178
254
  private printWelcome(options: ChatOptions): void {
179
255
  const sub = this.config.get('subscription');
180
256
 
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.4.0';
54
+ return '1.4.1';
55
55
  }
56
56
  const VERSION = getVersion();
57
57
 
@@ -19,17 +19,36 @@ export class FileUtils {
19
19
  private ignorePatterns: string[];
20
20
 
21
21
  constructor(projectRoot: string, ignorePatterns: string[] = []) {
22
- this.projectRoot = projectRoot;
22
+ // Resolve symlinks and normalize path for consistent behavior
23
+ try {
24
+ this.projectRoot = fs.realpathSync(projectRoot);
25
+ } catch {
26
+ // If realpath fails (e.g., path doesn't exist), use the original
27
+ this.projectRoot = path.resolve(projectRoot);
28
+ }
23
29
  this.ignorePatterns = ignorePatterns;
24
30
  }
25
31
 
26
32
  // Read file with metadata
27
33
  readFile(filePath: string): FileInfo | null {
28
34
  try {
29
- const absolutePath = path.isAbsolute(filePath)
35
+ let absolutePath = path.isAbsolute(filePath)
30
36
  ? filePath
31
37
  : path.join(this.projectRoot, filePath);
32
38
 
39
+ // Resolve symlinks for consistent path handling
40
+ try {
41
+ absolutePath = fs.realpathSync(absolutePath);
42
+ } catch {
43
+ // Keep original path if realpath fails
44
+ }
45
+
46
+ // Check if it's a regular file (not a directory or special file)
47
+ const stats = fs.statSync(absolutePath);
48
+ if (!stats.isFile()) {
49
+ return null;
50
+ }
51
+
33
52
  const content = fs.readFileSync(absolutePath, 'utf-8');
34
53
  const relativePath = path.relative(this.projectRoot, absolutePath);
35
54
 
@@ -88,13 +107,23 @@ export class FileUtils {
88
107
 
89
108
  // List project files
90
109
  async listFiles(pattern: string = '**/*'): Promise<string[]> {
91
- const files = await glob(pattern, {
92
- cwd: this.projectRoot,
93
- ignore: this.ignorePatterns,
94
- nodir: true,
95
- });
110
+ try {
111
+ // Check if project root exists
112
+ if (!fs.existsSync(this.projectRoot)) {
113
+ return [];
114
+ }
115
+
116
+ const files = await glob(pattern, {
117
+ cwd: this.projectRoot,
118
+ ignore: this.ignorePatterns,
119
+ nodir: true,
120
+ });
96
121
 
97
- return files;
122
+ return files;
123
+ } catch (error) {
124
+ // Return empty array on error (e.g., permission issues on Windows)
125
+ return [];
126
+ }
98
127
  }
99
128
 
100
129
  // Detect language from file extension
@@ -28,8 +28,20 @@ export class SessionManager {
28
28
  }
29
29
 
30
30
  private ensureDir(): void {
31
- if (!fs.existsSync(this.sessionsDir)) {
32
- fs.mkdirSync(this.sessionsDir, { recursive: true });
31
+ try {
32
+ if (!fs.existsSync(this.sessionsDir)) {
33
+ fs.mkdirSync(this.sessionsDir, { recursive: true, mode: 0o755 });
34
+ }
35
+ } catch (error: any) {
36
+ // On permission errors, try alternative location
37
+ if (error.code === 'EACCES' || error.code === 'EPERM') {
38
+ // Fallback to temp directory
39
+ this.sessionsDir = path.join(os.tmpdir(), 'vigthoria-sessions');
40
+ if (!fs.existsSync(this.sessionsDir)) {
41
+ fs.mkdirSync(this.sessionsDir, { recursive: true });
42
+ }
43
+ }
44
+ // Silently continue if directory creation fails
33
45
  }
34
46
  }
35
47
 
@@ -771,13 +771,34 @@ export class AgenticTools {
771
771
 
772
772
  try {
773
773
  const startTime = Date.now();
774
- const output = execSync(args.command, {
774
+ const os = require('os');
775
+ const platform = os.platform();
776
+
777
+ // Build exec options based on platform
778
+ const execOptions: any = {
775
779
  cwd,
776
780
  timeout,
777
781
  encoding: 'utf-8',
778
782
  maxBuffer: 10 * 1024 * 1024, // 10MB
779
783
  stdio: ['pipe', 'pipe', 'pipe'],
780
- });
784
+ // Ensure consistent environment
785
+ env: {
786
+ ...process.env,
787
+ // Prevent locale issues
788
+ LC_ALL: 'C',
789
+ LANG: 'C',
790
+ },
791
+ };
792
+
793
+ // Set shell based on platform for consistent behavior
794
+ if (platform === 'win32') {
795
+ execOptions.shell = true; // Use default cmd.exe on Windows
796
+ } else {
797
+ // Use /bin/sh on Unix-like systems (more portable than bash)
798
+ execOptions.shell = '/bin/sh';
799
+ }
800
+
801
+ const output = execSync(args.command, execOptions) as string;
781
802
 
782
803
  const duration = Date.now() - startTime;
783
804
 
@@ -833,10 +854,27 @@ export class AgenticTools {
833
854
  private grep(args: Record<string, string>): ToolResult {
834
855
  const searchPath = args.path ? this.resolvePath(args.path) : this.cwd;
835
856
  const include = args.include || '*';
857
+ const os = require('os');
858
+
859
+ // macOS uses BSD grep which has different options than GNU grep
860
+ const isMac = os.platform() === 'darwin';
836
861
 
837
- let cmd = `grep -rn --color=never "${args.pattern}" ${searchPath}`;
838
- if (args.include) {
839
- cmd = `grep -rn --color=never --include="${args.include}" "${args.pattern}" ${searchPath}`;
862
+ let cmd: string;
863
+ if (isMac) {
864
+ // BSD grep: use -r for recursive, -n for line numbers
865
+ // Note: BSD grep doesn't support --color=never, just omit it
866
+ if (args.include) {
867
+ cmd = `grep -rn --include="${args.include}" "${args.pattern}" "${searchPath}"`;
868
+ } else {
869
+ cmd = `grep -rn "${args.pattern}" "${searchPath}"`;
870
+ }
871
+ } else {
872
+ // GNU grep (Linux)
873
+ if (args.include) {
874
+ cmd = `grep -rn --color=never --include="${args.include}" "${args.pattern}" "${searchPath}"`;
875
+ } else {
876
+ cmd = `grep -rn --color=never "${args.pattern}" "${searchPath}"`;
877
+ }
840
878
  }
841
879
 
842
880
  try {
@@ -845,6 +883,7 @@ export class AgenticTools {
845
883
  encoding: 'utf-8',
846
884
  maxBuffer: 5 * 1024 * 1024,
847
885
  timeout: 30000,
886
+ env: { ...process.env, GREP_OPTIONS: '' }, // Prevent user's grep options from interfering
848
887
  });
849
888
 
850
889
  return { success: true, output: output.trim() };