vigthoria-cli 1.5.0 → 1.5.2
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/dist/commands/chat.d.ts +1 -0
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +17 -3
- package/dist/commands/chat.js.map +1 -1
- package/dist/utils/tools.d.ts +23 -1
- package/dist/utils/tools.d.ts.map +1 -1
- package/dist/utils/tools.js +284 -14
- package/dist/utils/tools.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/chat.ts +21 -3
- package/src/utils/tools.ts +356 -19
package/src/utils/tools.ts
CHANGED
|
@@ -78,7 +78,7 @@ export enum ToolErrorType {
|
|
|
78
78
|
export class AgenticTools {
|
|
79
79
|
private logger: Logger;
|
|
80
80
|
private cwd: string;
|
|
81
|
-
private permissionCallback: (action: string) => Promise<boolean>;
|
|
81
|
+
private permissionCallback: (action: string, options?: { batchApproval?: boolean }) => Promise<boolean | 'batch'>;
|
|
82
82
|
private autoApprove: boolean;
|
|
83
83
|
private undoStack: UndoOperation[] = [];
|
|
84
84
|
private maxUndoStack: number = 50;
|
|
@@ -88,11 +88,14 @@ export class AgenticTools {
|
|
|
88
88
|
maxDelayMs: 10000,
|
|
89
89
|
retryableErrors: ['ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND', 'ECONNREFUSED', 'EAI_AGAIN'],
|
|
90
90
|
};
|
|
91
|
+
|
|
92
|
+
// Session-based tool approvals - remembers which tools user approved for this turn
|
|
93
|
+
private sessionApprovedTools: Set<string> = new Set();
|
|
91
94
|
|
|
92
95
|
constructor(
|
|
93
96
|
logger: Logger,
|
|
94
97
|
cwd: string,
|
|
95
|
-
permissionCallback: (action: string) => Promise<boolean>,
|
|
98
|
+
permissionCallback: (action: string, options?: { batchApproval?: boolean }) => Promise<boolean | 'batch'>,
|
|
96
99
|
autoApprove: boolean = false
|
|
97
100
|
) {
|
|
98
101
|
this.logger = logger;
|
|
@@ -101,6 +104,20 @@ export class AgenticTools {
|
|
|
101
104
|
this.autoApprove = autoApprove;
|
|
102
105
|
}
|
|
103
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Clear session-approved tools (call this at the start of each new AI turn)
|
|
109
|
+
*/
|
|
110
|
+
clearSessionApprovals(): void {
|
|
111
|
+
this.sessionApprovedTools.clear();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get currently approved tools for this session
|
|
116
|
+
*/
|
|
117
|
+
getSessionApprovedTools(): string[] {
|
|
118
|
+
return Array.from(this.sessionApprovedTools);
|
|
119
|
+
}
|
|
120
|
+
|
|
104
121
|
/**
|
|
105
122
|
* Get the undo stack for inspection
|
|
106
123
|
*/
|
|
@@ -278,6 +295,34 @@ export class AgenticTools {
|
|
|
278
295
|
riskLevel: 'medium',
|
|
279
296
|
category: 'execute',
|
|
280
297
|
},
|
|
298
|
+
{
|
|
299
|
+
name: 'fetch_url',
|
|
300
|
+
description: 'Fetch content from a URL (web page, API endpoint). Works cross-platform on Windows, Mac, and Linux.',
|
|
301
|
+
parameters: [
|
|
302
|
+
{ name: 'url', description: 'URL to fetch (must be http or https)', required: true },
|
|
303
|
+
{ name: 'method', description: 'HTTP method (GET, POST, etc.)', required: false },
|
|
304
|
+
{ name: 'headers', description: 'JSON string of headers to send', required: false },
|
|
305
|
+
{ name: 'body', description: 'Request body for POST/PUT requests', required: false },
|
|
306
|
+
{ name: 'selector', description: 'CSS selector to extract specific content (for HTML)', required: false },
|
|
307
|
+
],
|
|
308
|
+
requiresPermission: true,
|
|
309
|
+
dangerous: false,
|
|
310
|
+
riskLevel: 'low',
|
|
311
|
+
category: 'read',
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
name: 'ssh_exec',
|
|
315
|
+
description: 'Execute a command on a remote server via SSH (useful for Unix commands from Windows)',
|
|
316
|
+
parameters: [
|
|
317
|
+
{ name: 'command', description: 'Command to execute on remote server', required: true },
|
|
318
|
+
{ name: 'host', description: 'SSH host (default: vigthoria-server)', required: false },
|
|
319
|
+
{ name: 'timeout', description: 'Timeout in seconds', required: false },
|
|
320
|
+
],
|
|
321
|
+
requiresPermission: true,
|
|
322
|
+
dangerous: true,
|
|
323
|
+
riskLevel: 'high',
|
|
324
|
+
category: 'execute',
|
|
325
|
+
},
|
|
281
326
|
];
|
|
282
327
|
}
|
|
283
328
|
|
|
@@ -303,16 +348,28 @@ export class AgenticTools {
|
|
|
303
348
|
|
|
304
349
|
// Check permission for dangerous/modifying actions
|
|
305
350
|
if (tool.requiresPermission && !this.autoApprove) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
351
|
+
// Check if this tool was already approved for this session/turn
|
|
352
|
+
if (this.sessionApprovedTools.has(call.tool)) {
|
|
353
|
+
// Already approved - skip permission prompt
|
|
354
|
+
this.logger.info(`${call.tool}: Auto-approved (batch)`);
|
|
355
|
+
} else {
|
|
356
|
+
const approved = await this.permissionCallback(
|
|
357
|
+
this.formatPermissionRequest(call, tool),
|
|
358
|
+
{ batchApproval: true }
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
if (approved === false) {
|
|
362
|
+
return {
|
|
363
|
+
success: false,
|
|
364
|
+
error: 'Permission denied by user',
|
|
365
|
+
canRetry: true,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// If user approved with 'batch' or just 'yes', remember for this session
|
|
370
|
+
if (approved === 'batch' || approved === true) {
|
|
371
|
+
this.sessionApprovedTools.add(call.tool);
|
|
372
|
+
}
|
|
316
373
|
}
|
|
317
374
|
}
|
|
318
375
|
|
|
@@ -394,6 +451,10 @@ export class AgenticTools {
|
|
|
394
451
|
return this.git(call.args);
|
|
395
452
|
case 'repo':
|
|
396
453
|
return this.repo(call.args);
|
|
454
|
+
case 'fetch_url':
|
|
455
|
+
return this.fetchUrl(call.args);
|
|
456
|
+
case 'ssh_exec':
|
|
457
|
+
return this.sshExec(call.args);
|
|
397
458
|
default:
|
|
398
459
|
return this.createErrorResult(
|
|
399
460
|
ToolErrorType.INVALID_ARGS,
|
|
@@ -507,6 +568,9 @@ export class AgenticTools {
|
|
|
507
568
|
msg += chalk.red(' The AI is requesting to execute commands on your system.\n');
|
|
508
569
|
}
|
|
509
570
|
|
|
571
|
+
// Add batch approval hint
|
|
572
|
+
msg += `\n${chalk.gray('Respond:')} ${chalk.white('[y]es')} ${chalk.gray('/')} ${chalk.white('[n]o')} ${chalk.gray('/')} ${chalk.cyan('[a]pprove all')} ${chalk.cyan(call.tool)} ${chalk.gray('for this turn')}\n`;
|
|
573
|
+
|
|
510
574
|
return msg;
|
|
511
575
|
}
|
|
512
576
|
|
|
@@ -727,10 +791,51 @@ export class AgenticTools {
|
|
|
727
791
|
/**
|
|
728
792
|
* Execute bash command with enhanced error handling
|
|
729
793
|
* SECURITY: Commands are sandboxed to workspace directory
|
|
794
|
+
* WINDOWS: Detects Unix-specific commands and suggests alternatives
|
|
730
795
|
*/
|
|
731
796
|
private bash(args: Record<string, string>): ToolResult {
|
|
732
797
|
const cwd = args.cwd ? this.resolvePath(args.cwd) : this.cwd;
|
|
733
798
|
const timeout = args.timeout ? parseInt(args.timeout) * 1000 : 30000;
|
|
799
|
+
const os = require('os');
|
|
800
|
+
const platform = os.platform();
|
|
801
|
+
|
|
802
|
+
// Unix-specific commands that don't exist on Windows
|
|
803
|
+
const unixOnlyCommands = [
|
|
804
|
+
'head', 'tail', 'grep', 'awk', 'sed', 'wc', 'cut', 'sort', 'uniq',
|
|
805
|
+
'xargs', 'find', 'chmod', 'chown', 'ln', 'df', 'du', 'ps', 'kill',
|
|
806
|
+
'top', 'htop', 'which', 'whereis', 'man', 'less', 'more', 'cat',
|
|
807
|
+
];
|
|
808
|
+
|
|
809
|
+
// Check if command uses Unix-only tools on Windows
|
|
810
|
+
if (platform === 'win32') {
|
|
811
|
+
const cmdParts = args.command.split(/[|&;]/);
|
|
812
|
+
for (const part of cmdParts) {
|
|
813
|
+
const firstWord = part.trim().split(/\s+/)[0].toLowerCase();
|
|
814
|
+
if (unixOnlyCommands.includes(firstWord)) {
|
|
815
|
+
return this.createErrorResult(
|
|
816
|
+
ToolErrorType.EXECUTION_FAILED,
|
|
817
|
+
`Command '${firstWord}' is not available on Windows`,
|
|
818
|
+
`Use the 'ssh_exec' tool to run Unix commands on the server, ` +
|
|
819
|
+
`or use 'fetch_url' for web requests. ` +
|
|
820
|
+
`PowerShell alternatives: dir (ls), type (cat), findstr (grep), select-string (grep)`
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// Check for pipe to Unix command
|
|
826
|
+
if (args.command.includes('|')) {
|
|
827
|
+
const pipedCommands = args.command.split('|').map(c => c.trim().split(/\s+/)[0].toLowerCase());
|
|
828
|
+
for (const cmd of pipedCommands) {
|
|
829
|
+
if (unixOnlyCommands.includes(cmd)) {
|
|
830
|
+
return this.createErrorResult(
|
|
831
|
+
ToolErrorType.EXECUTION_FAILED,
|
|
832
|
+
`Piped command '${cmd}' is not available on Windows`,
|
|
833
|
+
`Windows doesn't have '${cmd}'. Use 'ssh_exec' tool to run this command on the Vigthoria server instead.`
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
734
839
|
|
|
735
840
|
// SECURITY: Block dangerous commands that could access outside workspace
|
|
736
841
|
const blockedPatterns = [
|
|
@@ -771,8 +876,6 @@ export class AgenticTools {
|
|
|
771
876
|
|
|
772
877
|
try {
|
|
773
878
|
const startTime = Date.now();
|
|
774
|
-
const os = require('os');
|
|
775
|
-
const platform = os.platform();
|
|
776
879
|
|
|
777
880
|
// Build exec options based on platform
|
|
778
881
|
const execOptions: any = {
|
|
@@ -1051,6 +1154,217 @@ export class AgenticTools {
|
|
|
1051
1154
|
}
|
|
1052
1155
|
}
|
|
1053
1156
|
|
|
1157
|
+
/**
|
|
1158
|
+
* Fetch URL content - Cross-platform web fetching
|
|
1159
|
+
* Uses Node.js native fetch (available in Node 18+)
|
|
1160
|
+
*/
|
|
1161
|
+
private async fetchUrl(args: Record<string, string>): Promise<ToolResult> {
|
|
1162
|
+
const url = args.url;
|
|
1163
|
+
const method = (args.method || 'GET').toUpperCase();
|
|
1164
|
+
|
|
1165
|
+
// Validate URL
|
|
1166
|
+
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
1167
|
+
return this.createErrorResult(
|
|
1168
|
+
ToolErrorType.INVALID_ARGS,
|
|
1169
|
+
'URL must start with http:// or https://',
|
|
1170
|
+
'Provide a valid URL, e.g., https://example.com'
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
try {
|
|
1175
|
+
const fetchOptions: RequestInit = {
|
|
1176
|
+
method,
|
|
1177
|
+
headers: {
|
|
1178
|
+
'User-Agent': 'Vigthoria-CLI/1.5.1',
|
|
1179
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
1180
|
+
},
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
// Parse custom headers if provided
|
|
1184
|
+
if (args.headers) {
|
|
1185
|
+
try {
|
|
1186
|
+
const customHeaders = JSON.parse(args.headers);
|
|
1187
|
+
fetchOptions.headers = { ...fetchOptions.headers, ...customHeaders };
|
|
1188
|
+
} catch {
|
|
1189
|
+
this.logger.warn('Invalid headers JSON, using defaults');
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// Add body for POST/PUT requests
|
|
1194
|
+
if (args.body && ['POST', 'PUT', 'PATCH'].includes(method)) {
|
|
1195
|
+
fetchOptions.body = args.body;
|
|
1196
|
+
(fetchOptions.headers as Record<string, string>)['Content-Type'] = 'application/json';
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// Use AbortController for timeout
|
|
1200
|
+
const controller = new AbortController();
|
|
1201
|
+
const timeout = setTimeout(() => controller.abort(), 30000);
|
|
1202
|
+
fetchOptions.signal = controller.signal;
|
|
1203
|
+
|
|
1204
|
+
const response = await fetch(url, fetchOptions);
|
|
1205
|
+
clearTimeout(timeout);
|
|
1206
|
+
|
|
1207
|
+
if (!response.ok) {
|
|
1208
|
+
return this.createErrorResult(
|
|
1209
|
+
ToolErrorType.NETWORK_ERROR,
|
|
1210
|
+
`HTTP ${response.status}: ${response.statusText}`,
|
|
1211
|
+
`The server returned an error. Status: ${response.status}`
|
|
1212
|
+
);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
let content = await response.text();
|
|
1216
|
+
|
|
1217
|
+
// Extract content based on selector if provided (basic HTML extraction)
|
|
1218
|
+
if (args.selector && content.includes('<')) {
|
|
1219
|
+
// Basic extraction - for title, meta, etc.
|
|
1220
|
+
const selector = args.selector.toLowerCase();
|
|
1221
|
+
|
|
1222
|
+
if (selector === 'title') {
|
|
1223
|
+
const match = content.match(/<title[^>]*>([^<]*)<\/title>/i);
|
|
1224
|
+
content = match ? match[1].trim() : 'No title found';
|
|
1225
|
+
} else if (selector === 'meta') {
|
|
1226
|
+
const matches = content.match(/<meta[^>]+>/gi) || [];
|
|
1227
|
+
content = matches.join('\n');
|
|
1228
|
+
} else if (selector === 'body') {
|
|
1229
|
+
const match = content.match(/<body[^>]*>([\s\S]*)<\/body>/i);
|
|
1230
|
+
content = match ? match[1].replace(/<script[\s\S]*?<\/script>/gi, '').replace(/<style[\s\S]*?<\/style>/gi, '').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim() : content;
|
|
1231
|
+
} else if (selector.startsWith('h')) {
|
|
1232
|
+
const regex = new RegExp(`<${selector}[^>]*>([^<]*)</${selector}>`, 'gi');
|
|
1233
|
+
const matches: string[] = [];
|
|
1234
|
+
let match;
|
|
1235
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1236
|
+
matches.push(match[1].trim());
|
|
1237
|
+
}
|
|
1238
|
+
content = matches.length > 0 ? matches.join('\n') : `No ${selector} tags found`;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// Truncate if too long
|
|
1243
|
+
if (content.length > 50000) {
|
|
1244
|
+
content = content.substring(0, 50000) + '\n... (truncated, content too long)';
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
return {
|
|
1248
|
+
success: true,
|
|
1249
|
+
output: content,
|
|
1250
|
+
metadata: {
|
|
1251
|
+
url,
|
|
1252
|
+
status: response.status,
|
|
1253
|
+
contentType: response.headers.get('content-type') || 'unknown',
|
|
1254
|
+
contentLength: content.length,
|
|
1255
|
+
},
|
|
1256
|
+
};
|
|
1257
|
+
} catch (error: any) {
|
|
1258
|
+
if (error.name === 'AbortError') {
|
|
1259
|
+
return this.createErrorResult(
|
|
1260
|
+
ToolErrorType.TIMEOUT,
|
|
1261
|
+
'Request timed out after 30 seconds',
|
|
1262
|
+
'The server took too long to respond. Try again or use a different URL.'
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
return this.createErrorResult(
|
|
1267
|
+
ToolErrorType.NETWORK_ERROR,
|
|
1268
|
+
error.message,
|
|
1269
|
+
'Check your internet connection and ensure the URL is correct.'
|
|
1270
|
+
);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* Execute command via SSH on remote server
|
|
1276
|
+
* Useful for running Unix commands from Windows
|
|
1277
|
+
*/
|
|
1278
|
+
private async sshExec(args: Record<string, string>): Promise<ToolResult> {
|
|
1279
|
+
const command = args.command;
|
|
1280
|
+
const host = args.host || 'vigthoria-server';
|
|
1281
|
+
const timeout = args.timeout ? parseInt(args.timeout) * 1000 : 60000;
|
|
1282
|
+
|
|
1283
|
+
// Security checks for SSH commands
|
|
1284
|
+
const blockedPatterns = [
|
|
1285
|
+
/\brm\s+-rf?\s+\//i, // Dangerous rm commands
|
|
1286
|
+
/\bsudo\s+rm/i, // Sudo rm
|
|
1287
|
+
/>\s*\/dev\/sd/i, // Writing to disk devices
|
|
1288
|
+
/\bdd\s+.*of=\/dev/i, // dd to devices
|
|
1289
|
+
/\bmkfs/i, // Format filesystems
|
|
1290
|
+
/shutdown|reboot|halt|poweroff/i, // System control
|
|
1291
|
+
];
|
|
1292
|
+
|
|
1293
|
+
for (const pattern of blockedPatterns) {
|
|
1294
|
+
if (pattern.test(command)) {
|
|
1295
|
+
return this.createErrorResult(
|
|
1296
|
+
ToolErrorType.PERMISSION_DENIED,
|
|
1297
|
+
'This command is blocked for security reasons',
|
|
1298
|
+
'Dangerous system commands cannot be executed via SSH.'
|
|
1299
|
+
);
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
try {
|
|
1304
|
+
const os = require('os');
|
|
1305
|
+
const platform = os.platform();
|
|
1306
|
+
|
|
1307
|
+
let sshCommand: string;
|
|
1308
|
+
let execOptions: any = {
|
|
1309
|
+
encoding: 'utf-8',
|
|
1310
|
+
timeout,
|
|
1311
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
1312
|
+
};
|
|
1313
|
+
|
|
1314
|
+
if (platform === 'win32') {
|
|
1315
|
+
// On Windows, use the ssh command from OpenSSH
|
|
1316
|
+
sshCommand = `ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=10 ${host} "${command.replace(/"/g, '\\"')}"`;
|
|
1317
|
+
execOptions.shell = true;
|
|
1318
|
+
} else {
|
|
1319
|
+
// On Unix-like systems
|
|
1320
|
+
sshCommand = `ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=10 ${host} '${command.replace(/'/g, "'\\''")}'`;
|
|
1321
|
+
execOptions.shell = '/bin/sh';
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
const output = execSync(sshCommand, execOptions) as string;
|
|
1325
|
+
|
|
1326
|
+
return {
|
|
1327
|
+
success: true,
|
|
1328
|
+
output: output.trim(),
|
|
1329
|
+
metadata: { host, command },
|
|
1330
|
+
};
|
|
1331
|
+
} catch (error: any) {
|
|
1332
|
+
// Check for common SSH errors
|
|
1333
|
+
const errorMsg = error.stderr || error.message || '';
|
|
1334
|
+
|
|
1335
|
+
if (errorMsg.includes('Connection refused') || errorMsg.includes('No route to host')) {
|
|
1336
|
+
return this.createErrorResult(
|
|
1337
|
+
ToolErrorType.NETWORK_ERROR,
|
|
1338
|
+
`Cannot connect to SSH host: ${host}`,
|
|
1339
|
+
'Check that the SSH host is correct and the server is running.'
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
if (errorMsg.includes('Permission denied') || errorMsg.includes('authentication')) {
|
|
1344
|
+
return this.createErrorResult(
|
|
1345
|
+
ToolErrorType.PERMISSION_DENIED,
|
|
1346
|
+
'SSH authentication failed',
|
|
1347
|
+
'Make sure you have SSH key authentication set up for this host.'
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
if (error.killed) {
|
|
1352
|
+
return this.createErrorResult(
|
|
1353
|
+
ToolErrorType.TIMEOUT,
|
|
1354
|
+
`SSH command timed out after ${timeout/1000}s`,
|
|
1355
|
+
'Try a simpler command or increase the timeout.'
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
return {
|
|
1360
|
+
success: false,
|
|
1361
|
+
output: error.stdout || '',
|
|
1362
|
+
error: errorMsg,
|
|
1363
|
+
suggestion: 'Check the command syntax and ensure SSH access is configured.',
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1054
1368
|
/**
|
|
1055
1369
|
* Resolve and SANITIZE path - prevent path traversal outside workspace
|
|
1056
1370
|
* SECURITY: All paths MUST stay within the workspace (cwd)
|
|
@@ -1274,21 +1588,44 @@ To use a tool, output a JSON block in a code fence with "tool" language:
|
|
|
1274
1588
|
{"tool": "read_file", "args": {"path": "src/index.js"}}
|
|
1275
1589
|
\`\`\`
|
|
1276
1590
|
|
|
1277
|
-
3.
|
|
1591
|
+
3. Fetch a web page:
|
|
1592
|
+
\`\`\`tool
|
|
1593
|
+
{"tool": "fetch_url", "args": {"url": "https://example.com", "selector": "title"}}
|
|
1594
|
+
\`\`\`
|
|
1595
|
+
|
|
1596
|
+
4. Run command on remote server (for Unix commands from Windows):
|
|
1278
1597
|
\`\`\`tool
|
|
1279
|
-
{"tool": "
|
|
1598
|
+
{"tool": "ssh_exec", "args": {"command": "curl -s https://example.com | head -20"}}
|
|
1280
1599
|
\`\`\`
|
|
1281
1600
|
|
|
1282
|
-
|
|
1601
|
+
5. Write a file:
|
|
1283
1602
|
\`\`\`tool
|
|
1284
1603
|
{"tool": "write_file", "args": {"path": "hello.py", "content": "print('Hello World')"}}
|
|
1285
1604
|
\`\`\`
|
|
1286
1605
|
|
|
1287
|
-
|
|
1606
|
+
## CRITICAL RULES:
|
|
1607
|
+
|
|
1608
|
+
### Cross-Platform Compatibility:
|
|
1609
|
+
- On Windows, Unix commands (head, tail, grep, awk, sed, wc) are NOT available
|
|
1610
|
+
- Use \`fetch_url\` for web requests instead of curl|grep
|
|
1611
|
+
- Use \`ssh_exec\` to run Unix commands on the Vigthoria server if needed
|
|
1612
|
+
- Use \`read_file\` instead of cat
|
|
1613
|
+
- Use \`list_dir\` instead of ls
|
|
1614
|
+
|
|
1615
|
+
### Handling Tool Failures:
|
|
1616
|
+
- If a tool fails, REPORT the failure honestly to the user
|
|
1617
|
+
- NEVER make up or hallucinate content when a tool fails
|
|
1618
|
+
- If you cannot access a URL, say "I was unable to fetch the URL because..."
|
|
1619
|
+
- If a command fails, explain what happened and suggest alternatives
|
|
1620
|
+
- Do NOT write analysis or comparison reports if you couldn't gather the actual data
|
|
1621
|
+
|
|
1622
|
+
### File Access:
|
|
1288
1623
|
- You can ONLY access files within the current project workspace
|
|
1289
1624
|
- Use relative paths (e.g., "src/file.js", "app.py", "./config.json")
|
|
1290
1625
|
- Never try to access system files or directories outside the workspace
|
|
1291
|
-
|
|
1626
|
+
|
|
1627
|
+
### Tool Names:
|
|
1628
|
+
- Use ONLY these exact tool names: list_dir, read_file, write_file, edit_file, bash, grep, glob, git, repo, fetch_url, ssh_exec
|
|
1292
1629
|
- The JSON must be valid with double quotes for all keys and string values
|
|
1293
1630
|
- After tool execution, you will receive results and can continue with the next step
|
|
1294
1631
|
- Explain what you're doing before using tools
|