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.
- package/README.md +59 -3
- package/dist/commands/chat.d.ts +4 -0
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +88 -16
- package/dist/commands/chat.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/utils/files.d.ts.map +1 -1
- package/dist/utils/files.js +37 -8
- package/dist/utils/files.js.map +1 -1
- package/dist/utils/session.d.ts.map +1 -1
- package/dist/utils/session.js +15 -2
- package/dist/utils/session.js.map +1 -1
- package/dist/utils/tools.d.ts.map +1 -1
- package/dist/utils/tools.js +44 -5
- package/dist/utils/tools.js.map +1 -1
- package/install.ps1 +290 -0
- package/install.sh +68 -9
- package/package.json +1 -1
- package/src/commands/chat.ts +92 -16
- package/src/index.ts +1 -1
- package/src/utils/files.ts +37 -8
- package/src/utils/session.ts +14 -2
- package/src/utils/tools.ts +44 -5
package/src/commands/chat.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
131
|
+
projectPath, // Use resolved path
|
|
120
132
|
options.model,
|
|
121
133
|
this.agentMode
|
|
122
134
|
);
|
|
123
135
|
}
|
|
124
136
|
|
|
125
|
-
// Get project context
|
|
126
|
-
|
|
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,
|
|
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
|
|
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
|
|
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
package/src/utils/files.ts
CHANGED
|
@@ -19,17 +19,36 @@ export class FileUtils {
|
|
|
19
19
|
private ignorePatterns: string[];
|
|
20
20
|
|
|
21
21
|
constructor(projectRoot: string, ignorePatterns: string[] = []) {
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
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
|
package/src/utils/session.ts
CHANGED
|
@@ -28,8 +28,20 @@ export class SessionManager {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
private ensureDir(): void {
|
|
31
|
-
|
|
32
|
-
fs.
|
|
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
|
|
package/src/utils/tools.ts
CHANGED
|
@@ -771,13 +771,34 @@ export class AgenticTools {
|
|
|
771
771
|
|
|
772
772
|
try {
|
|
773
773
|
const startTime = Date.now();
|
|
774
|
-
const
|
|
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
|
|
838
|
-
if (
|
|
839
|
-
|
|
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() };
|