vexi-cli 0.5.3 → 0.5.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.
- package/dist/agent.js +53 -0
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { basename } from 'node:path';
|
|
18
18
|
import { platform } from 'node:os';
|
|
19
|
+
import { exec as cpExec } from 'node:child_process';
|
|
19
20
|
import { input, select, confirm } from '@inquirer/prompts';
|
|
20
21
|
import * as nodeRl from 'node:readline';
|
|
21
22
|
import ora from 'ora';
|
|
@@ -29,6 +30,26 @@ import { McpManager, parseToolCall } from './mcp/client.js';
|
|
|
29
30
|
import { loadMcpConfig } from './mcp/config.js';
|
|
30
31
|
import { ARABIC_RTL_NOTE, getStrings, t } from './i18n/index.js';
|
|
31
32
|
import { accent, dim, err, ok, printBanner, printStatusLine, userPrompt, vexiLabel, warn } from './ui/index.js';
|
|
33
|
+
/** Extract shell commands from fenced code blocks in an AI reply. */
|
|
34
|
+
function extractShellBlocks(reply) {
|
|
35
|
+
const blocks = [];
|
|
36
|
+
const re = /```(?:bash|sh|shell|cmd|powershell|ps1)\n([\s\S]*?)```/gi;
|
|
37
|
+
let m;
|
|
38
|
+
while ((m = re.exec(reply)) !== null) {
|
|
39
|
+
const code = m[1].trim();
|
|
40
|
+
if (code)
|
|
41
|
+
blocks.push(code);
|
|
42
|
+
}
|
|
43
|
+
return blocks;
|
|
44
|
+
}
|
|
45
|
+
/** Run a shell command and return { stdout, stderr, code }. */
|
|
46
|
+
function runCommand(cmd, cwd) {
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
cpExec(cmd, { cwd, shell: process.platform === 'win32' ? 'powershell.exe' : '/bin/sh', maxBuffer: 1024 * 1024 * 4 }, (err, stdout, stderr) => {
|
|
49
|
+
resolve({ stdout: stdout ?? '', stderr: stderr ?? '', code: err?.code ?? 0 });
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
32
53
|
/**
|
|
33
54
|
* Read a user message with multi-line paste support.
|
|
34
55
|
* Creates a fresh readline interface per call so it never conflicts
|
|
@@ -258,6 +279,31 @@ export async function runAgent(opts) {
|
|
|
258
279
|
process.stdout.write('\n\n');
|
|
259
280
|
history.push({ role: 'assistant', content: reply });
|
|
260
281
|
recorder.add('assistant', reply);
|
|
282
|
+
// ── Auto-run shell commands suggested by the AI ──────────────
|
|
283
|
+
const shellBlocks = extractShellBlocks(reply);
|
|
284
|
+
for (const cmd of shellBlocks) {
|
|
285
|
+
console.log(accent('▶ run? ') + dim(cmd.slice(0, 120) + (cmd.length > 120 ? '…' : '')));
|
|
286
|
+
const yes = await confirm({ message: 'Execute', default: true }).catch(() => false);
|
|
287
|
+
if (!yes) {
|
|
288
|
+
history.push({ role: 'user', content: `COMMAND SKIPPED: ${cmd}` });
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
const runSpinner = ora({ text: dim('running…'), spinner: 'dots' }).start();
|
|
292
|
+
const { stdout, stderr, code } = await runCommand(cmd, root);
|
|
293
|
+
runSpinner.stop();
|
|
294
|
+
const output = [
|
|
295
|
+
stdout.trim() ? `STDOUT:\n${stdout.trim()}` : '',
|
|
296
|
+
stderr.trim() ? `STDERR:\n${stderr.trim()}` : '',
|
|
297
|
+
`EXIT CODE: ${code}`,
|
|
298
|
+
].filter(Boolean).join('\n');
|
|
299
|
+
console.log(code === 0 ? ok('✓ done') : err(`✗ exit ${code}`));
|
|
300
|
+
if (stdout.trim())
|
|
301
|
+
console.log(dim(stdout.trim().slice(0, 800)));
|
|
302
|
+
if (stderr.trim())
|
|
303
|
+
console.log(warn(stderr.trim().slice(0, 400)));
|
|
304
|
+
history.push({ role: 'user', content: `COMMAND RESULT (${cmd.slice(0, 60)}):\n${output.slice(0, 6000)}` });
|
|
305
|
+
recorder.add('user', `COMMAND RESULT:\n${output.slice(0, 6000)}`);
|
|
306
|
+
}
|
|
261
307
|
// MCP tool call requested by the model?
|
|
262
308
|
const call = mcp.tools.length > 0 && round < 5 ? parseToolCall(reply) : null;
|
|
263
309
|
if (!call)
|
|
@@ -353,6 +399,13 @@ function buildSystemPrompt(lang, projectBlock, skillsText, memoryText, mcpText)
|
|
|
353
399
|
'Format code in fenced Markdown blocks with the language tag.',
|
|
354
400
|
`Environment: OS=${platform()}, cwd=${process.cwd()}.`,
|
|
355
401
|
`The user's preferred language is ${langNames[lang]}; reply in that language unless asked otherwise (code and identifiers stay in English).`,
|
|
402
|
+
'',
|
|
403
|
+
'## Command execution',
|
|
404
|
+
'You can run shell commands directly. Wrap any command you want to execute in a fenced code block tagged `bash` or `sh`.',
|
|
405
|
+
'Vexi will show the command to the user, ask for confirmation, execute it, and report the output back to you.',
|
|
406
|
+
'Use this to: install dependencies, build projects, run tests, start servers, scaffold files, etc.',
|
|
407
|
+
'Example: to install deps write: ```bash\nnpm install\n```',
|
|
408
|
+
'After seeing the output, continue helping based on the result.',
|
|
356
409
|
];
|
|
357
410
|
if (projectBlock)
|
|
358
411
|
parts.push('', '## Project map', projectBlock);
|