Node's first framework for building immersive CLI apps.
—
Plugin architecture for importing command libraries and extending Vorpal functionality.
Imports a library of Vorpal API commands.
/**
* Imports a library of Vorpal API commands
* @param commands - Extension module (function, string path, array, or object)
* @param options - Optional configuration for the extension
* @returns Vorpal instance for chaining
*/
function use(commands: ExtensionInput, options?: ExtensionOptions): Vorpal;
type ExtensionInput =
| ((vorpal: Vorpal) => void) // Function that extends vorpal
| string // Module path to require
| ExtensionInput[] // Array of extensions
| { [key: string]: any }; // Extension object
interface ExtensionOptions {
[key: string]: any; // Extension-specific options
}Usage Examples:
const vorpal = require('vorpal')();
// Load extension by function
function myExtension(vorpal) {
vorpal
.command('hello', 'Says hello')
.action(function(args, callback) {
this.log('Hello from extension!');
callback();
});
vorpal
.command('goodbye', 'Says goodbye')
.action(function(args, callback) {
this.log('Goodbye from extension!');
callback();
});
}
vorpal.use(myExtension);
// Load extension by module path
vorpal.use('./my-extension'); // Requires local file
vorpal.use('vorpal-grep'); // Requires npm module
// Load multiple extensions
vorpal.use([
'./commands/file-commands',
'./commands/network-commands',
myExtension
]);
// Load extension with options
vorpal.use(myExtension, {
prefix: 'ext:',
verbose: true
});The most common way to create extensions:
// my-extension.js
function myExtension(vorpal, options) {
const prefix = options?.prefix || '';
vorpal
.command(`${prefix}status`, 'Shows application status')
.action(function(args, callback) {
this.log('Status: Running');
this.log('Uptime:', process.uptime());
callback();
});
vorpal
.command(`${prefix}restart`, 'Restarts the application')
.option('-f, --force', 'Force restart without confirmation')
.action(function(args, callback) {
if (args.options.force) {
this.log('Force restarting...');
callback();
} else {
this.prompt({
type: 'confirm',
name: 'confirm',
message: 'Are you sure you want to restart?'
}, (result) => {
if (result.confirm) {
this.log('Restarting...');
} else {
this.log('Restart cancelled');
}
callback();
});
}
});
}
module.exports = myExtension;
// Usage
const vorpal = require('vorpal')();
vorpal.use(require('./my-extension'), { prefix: 'app:' });Extensions can also be objects with specific structure:
// extension-object.js
const extensionObject = {
// Optional initialization function
init: function(vorpal, options) {
console.log('Extension initialized with options:', options);
},
// Commands definition
commands: [
{
name: 'list-files [directory]',
description: 'Lists files in directory',
action: function(args, callback) {
const fs = require('fs');
const dir = args.directory || process.cwd();
fs.readdir(dir, (err, files) => {
if (err) {
this.log('Error:', err.message);
} else {
this.log('Files in', dir + ':');
files.forEach(file => this.log('-', file));
}
callback();
});
}
},
{
name: 'current-time',
description: 'Shows current time',
action: function(args, callback) {
this.log('Current time:', new Date().toLocaleString());
callback();
}
}
]
};
module.exports = extensionObject;More complex extensions using classes:
// database-extension.js
class DatabaseExtension {
constructor(options = {}) {
this.connectionString = options.connectionString;
this.timeout = options.timeout || 5000;
}
install(vorpal) {
const self = this;
vorpal
.command('db:connect', 'Connect to database')
.action(function(args, callback) {
self.connect(this, callback);
});
vorpal
.command('db:query <sql>', 'Execute SQL query')
.action(function(args, callback) {
self.query(this, args.sql, callback);
});
vorpal
.command('db:status', 'Show database status')
.action(function(args, callback) {
self.status(this, callback);
});
}
connect(context, callback) {
context.log('Connecting to database...');
// Database connection logic
setTimeout(() => {
context.log('Connected successfully');
callback();
}, 1000);
}
query(context, sql, callback) {
context.log(`Executing: ${sql}`);
// Query execution logic
setTimeout(() => {
context.log('Query completed');
callback();
}, 500);
}
status(context, callback) {
context.log('Database Status: Connected');
context.log('Connection:', this.connectionString);
callback();
}
}
// Export function that creates and installs the extension
module.exports = function(options) {
return function(vorpal) {
const extension = new DatabaseExtension(options);
extension.install(vorpal);
};
};
// Usage
const vorpal = require('vorpal')();
const dbExtension = require('./database-extension');
vorpal.use(dbExtension({
connectionString: 'mongodb://localhost:27017/mydb',
timeout: 10000
}));Many community extensions are available:
const vorpal = require('vorpal')();
// File system commands
vorpal.use('vorpal-less'); // less command for viewing files
vorpal.use('vorpal-grep'); // grep command for searching
// Network utilities
vorpal.use('vorpal-tour'); // Interactive tours/tutorials
// Development tools
vorpal.use('vorpal-watch'); // File watching commandsSplit complex functionality across multiple extension files:
// commands/index.js - Main extension loader
const fileCommands = require('./file-commands');
const networkCommands = require('./network-commands');
const systemCommands = require('./system-commands');
module.exports = function(options = {}) {
return function(vorpal) {
if (options.includeFiles !== false) {
vorpal.use(fileCommands);
}
if (options.includeNetwork !== false) {
vorpal.use(networkCommands);
}
if (options.includeSystem !== false) {
vorpal.use(systemCommands);
}
};
};
// Usage
const vorpal = require('vorpal')();
const myCommands = require('./commands');
vorpal.use(myCommands({
includeNetwork: false // Skip network commands
}));Extensions can maintain shared state:
// stateful-extension.js
function createStatefulExtension() {
// Shared state across commands
const state = {
users: [],
currentUser: null,
settings: {}
};
return function(vorpal) {
vorpal
.command('user:add <name>', 'Add a user')
.action(function(args, callback) {
state.users.push({
name: args.name,
id: Date.now(),
created: new Date()
});
this.log(`User ${args.name} added`);
callback();
});
vorpal
.command('user:list', 'List all users')
.action(function(args, callback) {
if (state.users.length === 0) {
this.log('No users found');
} else {
this.log('Users:');
state.users.forEach(user => {
this.log(`- ${user.name} (ID: ${user.id})`);
});
}
callback();
});
vorpal
.command('user:select <name>', 'Select current user')
.action(function(args, callback) {
const user = state.users.find(u => u.name === args.name);
if (user) {
state.currentUser = user;
this.log(`Selected user: ${user.name}`);
} else {
this.log(`User ${args.name} not found`);
}
callback();
});
vorpal
.command('user:current', 'Show current user')
.action(function(args, callback) {
if (state.currentUser) {
this.log('Current user:', state.currentUser.name);
} else {
this.log('No user selected');
}
callback();
});
};
}
module.exports = createStatefulExtension;
// Usage
const vorpal = require('vorpal')();
const statefulExtension = require('./stateful-extension');
vorpal.use(statefulExtension());Prefix commands to avoid conflicts:
function myExtension(vorpal, options) {
const prefix = options?.prefix || 'ext:';
vorpal
.command(`${prefix}command1`, 'Description')
.action(function(args, callback) {
// Command logic
callback();
});
}Make extensions configurable:
function configurableExtension(vorpal, options = {}) {
const config = {
timeout: options.timeout || 5000,
verbose: options.verbose || false,
prefix: options.prefix || '',
...options
};
// Use config throughout the extension
}Handle errors gracefully in extensions:
function robustExtension(vorpal) {
vorpal
.command('risky-command', 'Might fail')
.action(function(args, callback) {
try {
// Risky operation
this.log('Success!');
callback();
} catch (error) {
this.log('Error:', error.message);
callback(error);
}
});
}// file-manager-extension.js
const fs = require('fs');
const path = require('path');
function fileManagerExtension(vorpal, options = {}) {
const config = {
showHidden: options.showHidden || false,
defaultPath: options.defaultPath || process.cwd(),
...options
};
// Shared state
let currentPath = config.defaultPath;
vorpal
.command('fm:pwd', 'Show current directory')
.action(function(args, callback) {
this.log('Current directory:', currentPath);
callback();
});
vorpal
.command('fm:cd [directory]', 'Change directory')
.action(function(args, callback) {
const newPath = args.directory
? path.resolve(currentPath, args.directory)
: config.defaultPath;
fs.access(newPath, fs.constants.F_OK, (err) => {
if (err) {
this.log('Directory not found:', newPath);
} else {
currentPath = newPath;
this.log('Changed to:', currentPath);
}
callback();
});
});
vorpal
.command('fm:ls [pattern]', 'List files')
.option('-l, --long', 'Long format')
.option('-a, --all', 'Show hidden files')
.action(function(args, callback) {
const showHidden = args.options.all || config.showHidden;
const longFormat = args.options.long;
fs.readdir(currentPath, (err, files) => {
if (err) {
this.log('Error reading directory:', err.message);
callback();
return;
}
let filteredFiles = files;
if (!showHidden) {
filteredFiles = files.filter(file => !file.startsWith('.'));
}
if (args.pattern) {
const regex = new RegExp(args.pattern, 'i');
filteredFiles = filteredFiles.filter(file => regex.test(file));
}
if (longFormat) {
filteredFiles.forEach(file => {
const fullPath = path.join(currentPath, file);
fs.stat(fullPath, (err, stats) => {
if (!err) {
const size = stats.size;
const modified = stats.mtime.toLocaleDateString();
const type = stats.isDirectory() ? 'DIR' : 'FILE';
this.log(`${type.padEnd(4)} ${size.toString().padStart(8)} ${modified} ${file}`);
}
});
});
} else {
this.log(filteredFiles.join(' '));
}
callback();
});
});
}
module.exports = fileManagerExtension;
// Usage in main application
const vorpal = require('vorpal')();
const fileManager = require('./file-manager-extension');
vorpal.use(fileManager, {
showHidden: true,
defaultPath: '/home/user'
});
vorpal
.delimiter('app$')
.show();Install with Tessl CLI
npx tessl i tessl/npm-vorpal