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.
Files changed (2) hide show
  1. package/dist/agent.js +53 -0
  2. 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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vexi-cli",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "Open-source AI coding agent for your terminal. Bring your own key, zero config, multilingual.",
5
5
  "type": "module",
6
6
  "bin": {