@sethdouglasford/claude-flow
Version:
Claude Code Flow - Advanced AI-powered development workflows with SPARC methodology
1,467 lines (1,461 loc) • 5.03 MB
JavaScript
#!/usr/bin/env node
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from3, except, desc) => {
if (from3 && typeof from3 === "object" || typeof from3 === "function") {
for (let key of __getOwnPropNames(from3))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from3[key], enumerable: !(desc = __getOwnPropDesc(from3, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// dist/core/logger.js
var import_node_fs, path, import_node_buffer, import_node_process, LogLevel, Logger, logger2;
var init_logger = __esm({
"dist/core/logger.js"() {
import_node_fs = require("node:fs");
path = __toESM(require("node:path"), 1);
import_node_buffer = require("node:buffer");
import_node_process = __toESM(require("node:process"), 1);
(function(LogLevel2) {
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
})(LogLevel || (LogLevel = {}));
Logger = class _Logger {
static instance;
config;
context;
fileHandle;
currentFileSize = 0;
currentFileIndex = 0;
isClosing = false;
constructor(config = {
level: "info",
format: "text",
destination: "console"
}, context = {}) {
if ((config.destination === "file" || config.destination === "both") && !config.filePath) {
throw new Error("File path required for file logging");
}
this.config = config;
this.context = context;
}
/**
* Gets the singleton instance of the logger
*/
static getInstance(config) {
if (!_Logger.instance) {
if (!config) {
const isTestEnv = import_node_process.default.env.CLAUDE_FLOW_ENV === "test";
if (isTestEnv) {
throw new Error("Logger configuration required for initialization");
}
config = {
level: "info",
format: "text",
destination: "console"
};
}
_Logger.instance = new _Logger(config);
}
return _Logger.instance;
}
/**
* Updates logger configuration
*/
async configure(config) {
this.config = config;
if (this.fileHandle && config.destination !== "file" && config.destination !== "both") {
await this.fileHandle.close();
delete this.fileHandle;
}
}
debug(message, meta) {
this.log(LogLevel.DEBUG, message, meta);
}
info(message, meta) {
this.log(LogLevel.INFO, message, meta);
}
warn(message, meta) {
this.log(LogLevel.WARN, message, meta);
}
error(message, error3) {
this.log(LogLevel.ERROR, message, void 0, error3);
}
/**
* Creates a child logger with additional context
*/
child(context) {
return new _Logger(this.config, { ...this.context, ...context });
}
/**
* Properly close the logger and release resources
*/
async close() {
this.isClosing = true;
if (this.fileHandle) {
try {
await this.fileHandle.close();
} catch (error3) {
console.error("Error closing log file handle:", error3);
} finally {
delete this.fileHandle;
}
}
}
log(level, message, data, error3) {
if (!this.shouldLog(level)) {
return;
}
const entry = {
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
level: LogLevel[level],
message,
context: this.context,
data,
error: error3
};
const formatted = this.format(entry);
if (this.config.destination === "console" || this.config.destination === "both") {
this.writeToConsole(level, formatted);
}
if (this.config.destination === "file" || this.config.destination === "both") {
void this.writeToFile(formatted);
}
}
shouldLog(level) {
const configLevel = LogLevel[this.config.level.toUpperCase()];
return level >= configLevel;
}
format(entry) {
if (this.config.format === "json") {
const jsonEntry = { ...entry };
if (jsonEntry.error instanceof Error) {
jsonEntry.error = {
name: jsonEntry.error.name,
message: jsonEntry.error.message,
stack: jsonEntry.error.stack
};
}
return JSON.stringify(jsonEntry);
}
const contextStr = Object.keys(entry.context).length > 0 ? ` ${JSON.stringify(entry.context)}` : "";
const dataStr = entry.data !== void 0 ? ` ${JSON.stringify(entry.data)}` : "";
const errorStr = entry.error !== void 0 ? entry.error instanceof Error ? `
Error: ${entry.error.message}
Stack: ${entry.error.stack}` : ` Error: ${JSON.stringify(entry.error)}` : "";
return `[${entry.timestamp}] ${entry.level} ${entry.message}${contextStr}${dataStr}${errorStr}`;
}
writeToConsole(level, message) {
switch (level) {
case LogLevel.DEBUG:
console.debug(message);
break;
case LogLevel.INFO:
console.info(message);
break;
case LogLevel.WARN:
console.warn(message);
break;
case LogLevel.ERROR:
console.error(message);
break;
}
}
async writeToFile(message) {
if (!this.config.filePath || this.isClosing) {
return;
}
try {
if (await this.shouldRotate()) {
await this.rotate();
}
if (!this.fileHandle) {
this.fileHandle = await import_node_fs.promises.open(this.config.filePath, "a");
}
const data = import_node_buffer.Buffer.from(`${message}
`, "utf8");
await this.fileHandle.write(data);
this.currentFileSize += data.length;
} catch (error3) {
console.error("Failed to write to log file:", error3);
}
}
async shouldRotate() {
if (!this.config.maxFileSize || !this.config.filePath) {
return false;
}
try {
const stat = await import_node_fs.promises.stat(this.config.filePath);
return stat.size >= this.config.maxFileSize;
} catch {
return false;
}
}
async rotate() {
if (!this.config.filePath || !this.config.maxFiles) {
return;
}
if (this.fileHandle) {
await this.fileHandle.close();
delete this.fileHandle;
}
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
const rotatedPath = `${this.config.filePath}.${timestamp}`;
await import_node_fs.promises.rename(this.config.filePath, rotatedPath);
await this.cleanupOldFiles();
this.currentFileSize = 0;
}
async cleanupOldFiles() {
if (!this.config.filePath || !this.config.maxFiles) {
return;
}
const dir = path.dirname(this.config.filePath);
const baseFileName = path.basename(this.config.filePath);
try {
const entries = await import_node_fs.promises.readdir(dir, { withFileTypes: true });
const files = [];
for (const entry of entries) {
if (entry.isFile() && entry.name.startsWith(`${baseFileName}.`)) {
files.push(entry.name);
}
}
files.sort().reverse();
const filesToRemove = files.slice(this.config.maxFiles - 1);
for (const file of filesToRemove) {
await import_node_fs.promises.unlink(path.join(dir, file));
}
} catch (error3) {
console.error("Failed to cleanup old log files:", error3);
}
}
};
logger2 = Logger.getInstance();
}
});
// dist/config/config-manager.js
function isSEA() {
if (process.isSEA) {
return true;
}
const { execPath } = process;
const isNodeBinary = execPath.includes("node") && !execPath.includes("claude-flow");
return !isNodeBinary;
}
function getDefaultMemoryBackend() {
if (isSEA()) {
return "markdown";
}
return "hybrid";
}
var import_fs, import_path, import_os, DEFAULT_CONFIG, ConfigError, ConfigManager, configManager;
var init_config_manager = __esm({
"dist/config/config-manager.js"() {
import_fs = require("fs");
import_path = __toESM(require("path"), 1);
import_os = __toESM(require("os"), 1);
DEFAULT_CONFIG = {
orchestrator: {
maxConcurrentAgents: 10,
taskQueueSize: 100,
healthCheckInterval: 3e4,
shutdownTimeout: 3e4
},
terminal: {
type: "auto",
poolSize: 5,
recycleAfter: 10,
healthCheckInterval: 6e4,
commandTimeout: 3e5
},
memory: {
backend: "hybrid",
// Will be overridden in createDefaultConfig for SEA
cacheSizeMB: 100,
syncInterval: 5e3,
conflictResolution: "crdt",
retentionDays: 30
},
coordination: {
maxRetries: 3,
retryDelay: 1e3,
deadlockDetection: true,
resourceTimeout: 6e4,
messageTimeout: 3e4
},
mcp: {
transport: "stdio",
port: 3e3,
tlsEnabled: false
},
logging: {
level: "info",
format: "json",
destination: "console"
}
};
ConfigError = class extends Error {
constructor(message) {
super(message);
this.name = "ConfigError";
}
};
ConfigManager = class _ConfigManager {
static instance;
config;
configPath;
userConfigDir;
constructor() {
this.config = this.deepClone(DEFAULT_CONFIG);
this.userConfigDir = import_path.default.join(import_os.default.homedir(), ".claude-flow");
}
/**
* Gets the singleton instance
*/
static getInstance() {
if (!_ConfigManager.instance) {
_ConfigManager.instance = new _ConfigManager();
}
return _ConfigManager.instance;
}
/**
* Initialize configuration from file or create default
*/
async init(configPath = "claude-flow.config.json") {
try {
await this.load(configPath);
console.log(`\u2705 Configuration loaded from: ${configPath}`);
} catch (error3) {
await this.createDefaultConfig(configPath);
console.log(`\u2705 Default configuration created: ${configPath}`);
}
}
/**
* Creates a default configuration file
*/
async createDefaultConfig(configPath) {
const config = this.deepClone(DEFAULT_CONFIG);
const defaultBackend = getDefaultMemoryBackend();
if (defaultBackend !== config.memory.backend) {
config.memory.backend = defaultBackend;
console.log(`\u2139\uFE0F Using ${defaultBackend} backend for SEA (Single Executable) compatibility`);
}
const content = JSON.stringify(config, null, 2);
await import_fs.promises.writeFile(configPath, content, "utf8");
this.configPath = configPath;
}
/**
* Loads configuration from file
*/
async load(configPath) {
if (configPath) {
this.configPath = configPath;
}
if (!this.configPath) {
throw new ConfigError("No configuration file path specified");
}
try {
const content = await import_fs.promises.readFile(this.configPath, "utf8");
const fileConfig = JSON.parse(content);
this.config = this.deepMerge(DEFAULT_CONFIG, fileConfig);
this.loadFromEnv();
this.validate(this.config);
return this.config;
} catch (error3) {
if (error3.code === "ENOENT") {
throw new ConfigError(`Configuration file not found: ${this.configPath}`);
}
throw new ConfigError(`Failed to load configuration: ${error3.message}`);
}
}
/**
* Shows current configuration
*/
show() {
return this.deepClone(this.config);
}
/**
* Gets a configuration value by path
*/
get(path10) {
const keys = path10.split(".");
let current = this.config;
for (const key of keys) {
if (current && typeof current === "object" && key in current) {
current = current[key];
} else {
return void 0;
}
}
return current;
}
/**
* Sets a configuration value by path
*/
set(path10, value) {
const keys = path10.split(".");
let current = this.config;
for (let i3 = 0; i3 < keys.length - 1; i3++) {
const key = keys[i3];
if (!(key in current)) {
current[key] = {};
}
current = current[key];
}
const lastKey = keys[keys.length - 1];
current[lastKey] = value;
this.validate(this.config);
}
/**
* Saves current configuration to file
*/
async save(configPath) {
const savePath = configPath || this.configPath;
if (!savePath) {
throw new ConfigError("No configuration file path specified");
}
const content = JSON.stringify(this.config, null, 2);
await import_fs.promises.writeFile(savePath, content, "utf8");
}
/**
* Validates the configuration
*/
validate(config) {
if (config.orchestrator.maxConcurrentAgents < 1 || config.orchestrator.maxConcurrentAgents > 100) {
throw new ConfigError("orchestrator.maxConcurrentAgents must be between 1 and 100");
}
if (config.orchestrator.taskQueueSize < 1 || config.orchestrator.taskQueueSize > 1e4) {
throw new ConfigError("orchestrator.taskQueueSize must be between 1 and 10000");
}
if (!["auto", "vscode", "native"].includes(config.terminal.type)) {
throw new ConfigError("terminal.type must be one of: auto, vscode, native");
}
if (config.terminal.poolSize < 1 || config.terminal.poolSize > 50) {
throw new ConfigError("terminal.poolSize must be between 1 and 50");
}
if (!["sqlite", "markdown", "hybrid"].includes(config.memory.backend)) {
throw new ConfigError("memory.backend must be one of: sqlite, markdown, hybrid");
}
if (config.memory.cacheSizeMB < 1 || config.memory.cacheSizeMB > 1e4) {
throw new ConfigError("memory.cacheSizeMB must be between 1 and 10000");
}
if (config.coordination.maxRetries < 0 || config.coordination.maxRetries > 100) {
throw new ConfigError("coordination.maxRetries must be between 0 and 100");
}
if (!["stdio", "http", "websocket"].includes(config.mcp.transport)) {
throw new ConfigError("mcp.transport must be one of: stdio, http, websocket");
}
if (config.mcp.port < 1 || config.mcp.port > 65535) {
throw new ConfigError("mcp.port must be between 1 and 65535");
}
if (!["debug", "info", "warn", "error"].includes(config.logging.level)) {
throw new ConfigError("logging.level must be one of: debug, info, warn, error");
}
if (!["json", "text"].includes(config.logging.format)) {
throw new ConfigError("logging.format must be one of: json, text");
}
if (!["console", "file"].includes(config.logging.destination)) {
throw new ConfigError("logging.destination must be one of: console, file");
}
}
/**
* Loads configuration from environment variables
*/
loadFromEnv() {
const maxAgents = process.env.CLAUDE_FLOW_MAX_AGENTS;
if (maxAgents) {
this.config.orchestrator.maxConcurrentAgents = parseInt(maxAgents, 10);
}
const terminalType = process.env.CLAUDE_FLOW_TERMINAL_TYPE;
if (terminalType === "vscode" || terminalType === "native" || terminalType === "auto") {
this.config.terminal.type = terminalType;
}
const memoryBackend = process.env.CLAUDE_FLOW_MEMORY_BACKEND;
if (memoryBackend === "sqlite" || memoryBackend === "markdown" || memoryBackend === "hybrid") {
this.config.memory.backend = memoryBackend;
}
const mcpTransport = process.env.CLAUDE_FLOW_MCP_TRANSPORT;
if (mcpTransport === "stdio" || mcpTransport === "http" || mcpTransport === "websocket") {
this.config.mcp.transport = mcpTransport;
}
const mcpPort = process.env.CLAUDE_FLOW_MCP_PORT;
if (mcpPort) {
this.config.mcp.port = parseInt(mcpPort, 10);
}
const logLevel = process.env.CLAUDE_FLOW_LOG_LEVEL;
if (logLevel === "debug" || logLevel === "info" || logLevel === "warn" || logLevel === "error") {
this.config.logging.level = logLevel;
}
}
/**
* Deep clone helper
*/
deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
/**
* Deep merge helper
*/
deepMerge(target, source) {
const result = this.deepClone(target);
if (source.orchestrator) {
result.orchestrator = { ...result.orchestrator, ...source.orchestrator };
}
if (source.terminal) {
result.terminal = { ...result.terminal, ...source.terminal };
}
if (source.memory) {
result.memory = { ...result.memory, ...source.memory };
}
if (source.coordination) {
result.coordination = { ...result.coordination, ...source.coordination };
}
if (source.mcp) {
result.mcp = { ...result.mcp, ...source.mcp };
}
if (source.logging) {
result.logging = { ...result.logging, ...source.logging };
}
return result;
}
/**
* Gets available configuration templates
*/
getAvailableTemplates() {
return ["minimal", "default", "development", "production"];
}
/**
* Creates a configuration from a template
*/
createTemplate(templateName) {
const templates = {
minimal: {
orchestrator: {
maxConcurrentAgents: 5,
taskQueueSize: 50,
healthCheckInterval: 6e4,
shutdownTimeout: 1e4
},
terminal: {
type: "auto",
poolSize: 2,
recycleAfter: 5,
healthCheckInterval: 12e4,
commandTimeout: 3e5
}
},
development: {
logging: {
level: "debug",
format: "text",
destination: "console"
},
orchestrator: {
maxConcurrentAgents: 20,
taskQueueSize: 200,
healthCheckInterval: 15e3,
shutdownTimeout: 5e3
}
},
production: {
logging: {
level: "warn",
format: "json",
destination: "file"
},
memory: {
backend: "markdown",
cacheSizeMB: 500,
syncInterval: 1e4,
conflictResolution: "timestamp",
retentionDays: 90
}
}
};
const template = templates[templateName] || {};
return this.deepMerge(DEFAULT_CONFIG, template);
}
/**
* Gets format parsers for different config file formats
*/
getFormatParsers() {
return {
json: {
stringify: (obj) => JSON.stringify(obj, null, 2),
parse: (str) => JSON.parse(str)
},
yaml: {
stringify: (obj) => {
return this.toYAML(obj);
},
parse: (str) => {
throw new Error("YAML parsing not implemented");
}
},
toml: {
stringify: (obj) => {
throw new Error("TOML stringification not implemented");
},
parse: (str) => {
throw new Error("TOML parsing not implemented");
}
}
};
}
/**
* Validates a configuration file
*/
async validateFile(configPath) {
const errors = [];
try {
const content = await import_fs.promises.readFile(configPath, "utf8");
const config = JSON.parse(content);
try {
this.validate(config);
} catch (error3) {
errors.push(error3.message);
}
if (!config.orchestrator) {
errors.push("Missing required section: orchestrator");
}
if (!config.terminal) {
errors.push("Missing required section: terminal");
}
if (!config.memory) {
errors.push("Missing required section: memory");
}
return {
valid: errors.length === 0,
errors
};
} catch (error3) {
errors.push(`Failed to read or parse file: ${error3.message}`);
return {
valid: false,
errors
};
}
}
/**
* Backs up the current configuration
*/
async backup(backupPath) {
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
const backupFile = backupPath || import_path.default.join(this.userConfigDir, `backup-${timestamp}.json`);
await import_fs.promises.mkdir(import_path.default.dirname(backupFile), { recursive: true });
await import_fs.promises.writeFile(backupFile, JSON.stringify(this.config, null, 2), "utf8");
return backupFile;
}
/**
* Restores configuration from a backup
*/
async restore(backupPath) {
const content = await import_fs.promises.readFile(backupPath, "utf8");
const backupConfig = JSON.parse(content);
this.validate(backupConfig);
this.config = backupConfig;
if (this.configPath) {
await this.save();
}
}
/**
* Gets configuration path history
*/
getPathHistory() {
return this.configPath ? [this.configPath] : [];
}
/**
* Gets configuration change history
*/
getChangeHistory() {
return [];
}
/**
* Simple YAML converter (basic implementation)
*/
toYAML(obj, indent = 0) {
const spaces = " ".repeat(indent);
let result = "";
for (const [key, value] of Object.entries(obj)) {
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
result += `${spaces}${key}:
${this.toYAML(value, indent + 1)}`;
} else if (Array.isArray(value)) {
result += `${spaces}${key}:
`;
value.forEach((item) => {
result += `${spaces} - ${JSON.stringify(item)}
`;
});
} else {
result += `${spaces}${key}: ${JSON.stringify(value)}
`;
}
}
return result;
}
};
configManager = ConfigManager.getInstance();
}
});
// node_modules/commander/lib/error.js
var require_error = __commonJS({
"node_modules/commander/lib/error.js"(exports2) {
var CommanderError2 = class extends Error {
/**
* Constructs the CommanderError class
* @param {number} exitCode suggested exit code which could be used with process.exit
* @param {string} code an id string representing the error
* @param {string} message human-readable description of the error
* @constructor
*/
constructor(exitCode, code, message) {
super(message);
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.code = code;
this.exitCode = exitCode;
this.nestedError = void 0;
}
};
var InvalidArgumentError2 = class extends CommanderError2 {
/**
* Constructs the InvalidArgumentError class
* @param {string} [message] explanation of why argument is invalid
* @constructor
*/
constructor(message) {
super(1, "commander.invalidArgument", message);
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
}
};
exports2.CommanderError = CommanderError2;
exports2.InvalidArgumentError = InvalidArgumentError2;
}
});
// node_modules/commander/lib/argument.js
var require_argument = __commonJS({
"node_modules/commander/lib/argument.js"(exports2) {
var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
var Argument2 = class {
/**
* Initialize a new command argument with the given name and description.
* The default is that the argument is required, and you can explicitly
* indicate this with <> around the name. Put [] around the name for an optional argument.
*
* @param {string} name
* @param {string} [description]
*/
constructor(name, description) {
this.description = description || "";
this.variadic = false;
this.parseArg = void 0;
this.defaultValue = void 0;
this.defaultValueDescription = void 0;
this.argChoices = void 0;
switch (name[0]) {
case "<":
this.required = true;
this._name = name.slice(1, -1);
break;
case "[":
this.required = false;
this._name = name.slice(1, -1);
break;
default:
this.required = true;
this._name = name;
break;
}
if (this._name.length > 3 && this._name.slice(-3) === "...") {
this.variadic = true;
this._name = this._name.slice(0, -3);
}
}
/**
* Return argument name.
*
* @return {string}
*/
name() {
return this._name;
}
/**
* @api private
*/
_concatValue(value, previous) {
if (previous === this.defaultValue || !Array.isArray(previous)) {
return [value];
}
return previous.concat(value);
}
/**
* Set the default value, and optionally supply the description to be displayed in the help.
*
* @param {*} value
* @param {string} [description]
* @return {Argument}
*/
default(value, description) {
this.defaultValue = value;
this.defaultValueDescription = description;
return this;
}
/**
* Set the custom handler for processing CLI command arguments into argument values.
*
* @param {Function} [fn]
* @return {Argument}
*/
argParser(fn) {
this.parseArg = fn;
return this;
}
/**
* Only allow argument value to be one of choices.
*
* @param {string[]} values
* @return {Argument}
*/
choices(values) {
this.argChoices = values.slice();
this.parseArg = (arg, previous) => {
if (!this.argChoices.includes(arg)) {
throw new InvalidArgumentError2(`Allowed choices are ${this.argChoices.join(", ")}.`);
}
if (this.variadic) {
return this._concatValue(arg, previous);
}
return arg;
};
return this;
}
/**
* Make argument required.
*/
argRequired() {
this.required = true;
return this;
}
/**
* Make argument optional.
*/
argOptional() {
this.required = false;
return this;
}
};
function humanReadableArgName(arg) {
const nameOutput = arg.name() + (arg.variadic === true ? "..." : "");
return arg.required ? "<" + nameOutput + ">" : "[" + nameOutput + "]";
}
exports2.Argument = Argument2;
exports2.humanReadableArgName = humanReadableArgName;
}
});
// node_modules/commander/lib/help.js
var require_help = __commonJS({
"node_modules/commander/lib/help.js"(exports2) {
var { humanReadableArgName } = require_argument();
var Help2 = class {
constructor() {
this.helpWidth = void 0;
this.sortSubcommands = false;
this.sortOptions = false;
this.showGlobalOptions = false;
}
/**
* Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one.
*
* @param {Command} cmd
* @returns {Command[]}
*/
visibleCommands(cmd) {
const visibleCommands = cmd.commands.filter((cmd2) => !cmd2._hidden);
if (cmd._hasImplicitHelpCommand()) {
const [, helpName, helpArgs] = cmd._helpCommandnameAndArgs.match(/([^ ]+) *(.*)/);
const helpCommand2 = cmd.createCommand(helpName).helpOption(false);
helpCommand2.description(cmd._helpCommandDescription);
if (helpArgs) helpCommand2.arguments(helpArgs);
visibleCommands.push(helpCommand2);
}
if (this.sortSubcommands) {
visibleCommands.sort((a3, b3) => {
return a3.name().localeCompare(b3.name());
});
}
return visibleCommands;
}
/**
* Compare options for sort.
*
* @param {Option} a
* @param {Option} b
* @returns number
*/
compareOptions(a3, b3) {
const getSortKey = (option) => {
return option.short ? option.short.replace(/^-/, "") : option.long.replace(/^--/, "");
};
return getSortKey(a3).localeCompare(getSortKey(b3));
}
/**
* Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.
*
* @param {Command} cmd
* @returns {Option[]}
*/
visibleOptions(cmd) {
const visibleOptions = cmd.options.filter((option) => !option.hidden);
const showShortHelpFlag = cmd._hasHelpOption && cmd._helpShortFlag && !cmd._findOption(cmd._helpShortFlag);
const showLongHelpFlag = cmd._hasHelpOption && !cmd._findOption(cmd._helpLongFlag);
if (showShortHelpFlag || showLongHelpFlag) {
let helpOption;
if (!showShortHelpFlag) {
helpOption = cmd.createOption(cmd._helpLongFlag, cmd._helpDescription);
} else if (!showLongHelpFlag) {
helpOption = cmd.createOption(cmd._helpShortFlag, cmd._helpDescription);
} else {
helpOption = cmd.createOption(cmd._helpFlags, cmd._helpDescription);
}
visibleOptions.push(helpOption);
}
if (this.sortOptions) {
visibleOptions.sort(this.compareOptions);
}
return visibleOptions;
}
/**
* Get an array of the visible global options. (Not including help.)
*
* @param {Command} cmd
* @returns {Option[]}
*/
visibleGlobalOptions(cmd) {
if (!this.showGlobalOptions) return [];
const globalOptions = [];
for (let ancestorCmd = cmd.parent; ancestorCmd; ancestorCmd = ancestorCmd.parent) {
const visibleOptions = ancestorCmd.options.filter((option) => !option.hidden);
globalOptions.push(...visibleOptions);
}
if (this.sortOptions) {
globalOptions.sort(this.compareOptions);
}
return globalOptions;
}
/**
* Get an array of the arguments if any have a description.
*
* @param {Command} cmd
* @returns {Argument[]}
*/
visibleArguments(cmd) {
if (cmd._argsDescription) {
cmd.registeredArguments.forEach((argument) => {
argument.description = argument.description || cmd._argsDescription[argument.name()] || "";
});
}
if (cmd.registeredArguments.find((argument) => argument.description)) {
return cmd.registeredArguments;
}
return [];
}
/**
* Get the command term to show in the list of subcommands.
*
* @param {Command} cmd
* @returns {string}
*/
subcommandTerm(cmd) {
const args = cmd.registeredArguments.map((arg) => humanReadableArgName(arg)).join(" ");
return cmd._name + (cmd._aliases[0] ? "|" + cmd._aliases[0] : "") + (cmd.options.length ? " [options]" : "") + // simplistic check for non-help option
(args ? " " + args : "");
}
/**
* Get the option term to show in the list of options.
*
* @param {Option} option
* @returns {string}
*/
optionTerm(option) {
return option.flags;
}
/**
* Get the argument term to show in the list of arguments.
*
* @param {Argument} argument
* @returns {string}
*/
argumentTerm(argument) {
return argument.name();
}
/**
* Get the longest command term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestSubcommandTermLength(cmd, helper) {
return helper.visibleCommands(cmd).reduce((max, command) => {
return Math.max(max, helper.subcommandTerm(command).length);
}, 0);
}
/**
* Get the longest option term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestOptionTermLength(cmd, helper) {
return helper.visibleOptions(cmd).reduce((max, option) => {
return Math.max(max, helper.optionTerm(option).length);
}, 0);
}
/**
* Get the longest global option term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestGlobalOptionTermLength(cmd, helper) {
return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
return Math.max(max, helper.optionTerm(option).length);
}, 0);
}
/**
* Get the longest argument term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestArgumentTermLength(cmd, helper) {
return helper.visibleArguments(cmd).reduce((max, argument) => {
return Math.max(max, helper.argumentTerm(argument).length);
}, 0);
}
/**
* Get the command usage to be displayed at the top of the built-in help.
*
* @param {Command} cmd
* @returns {string}
*/
commandUsage(cmd) {
let cmdName = cmd._name;
if (cmd._aliases[0]) {
cmdName = cmdName + "|" + cmd._aliases[0];
}
let ancestorCmdNames = "";
for (let ancestorCmd = cmd.parent; ancestorCmd; ancestorCmd = ancestorCmd.parent) {
ancestorCmdNames = ancestorCmd.name() + " " + ancestorCmdNames;
}
return ancestorCmdNames + cmdName + " " + cmd.usage();
}
/**
* Get the description for the command.
*
* @param {Command} cmd
* @returns {string}
*/
commandDescription(cmd) {
return cmd.description();
}
/**
* Get the subcommand summary to show in the list of subcommands.
* (Fallback to description for backwards compatibility.)
*
* @param {Command} cmd
* @returns {string}
*/
subcommandDescription(cmd) {
return cmd.summary() || cmd.description();
}
/**
* Get the option description to show in the list of options.
*
* @param {Option} option
* @return {string}
*/
optionDescription(option) {
const extraInfo = [];
if (option.argChoices) {
extraInfo.push(
// use stringify to match the display of the default value
`choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(", ")}`
);
}
if (option.defaultValue !== void 0) {
const showDefault = option.required || option.optional || option.isBoolean() && typeof option.defaultValue === "boolean";
if (showDefault) {
extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`);
}
}
if (option.presetArg !== void 0 && option.optional) {
extraInfo.push(`preset: ${JSON.stringify(option.presetArg)}`);
}
if (option.envVar !== void 0) {
extraInfo.push(`env: ${option.envVar}`);
}
if (extraInfo.length > 0) {
return `${option.description} (${extraInfo.join(", ")})`;
}
return option.description;
}
/**
* Get the argument description to show in the list of arguments.
*
* @param {Argument} argument
* @return {string}
*/
argumentDescription(argument) {
const extraInfo = [];
if (argument.argChoices) {
extraInfo.push(
// use stringify to match the display of the default value
`choices: ${argument.argChoices.map((choice) => JSON.stringify(choice)).join(", ")}`
);
}
if (argument.defaultValue !== void 0) {
extraInfo.push(`default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`);
}
if (extraInfo.length > 0) {
const extraDescripton = `(${extraInfo.join(", ")})`;
if (argument.description) {
return `${argument.description} ${extraDescripton}`;
}
return extraDescripton;
}
return argument.description;
}
/**
* Generate the built-in help text.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {string}
*/
formatHelp(cmd, helper) {
const termWidth = helper.padWidth(cmd, helper);
const helpWidth = helper.helpWidth || 80;
const itemIndentWidth = 2;
const itemSeparatorWidth = 2;
function formatItem(term, description) {
if (description) {
const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`;
return helper.wrap(fullText, helpWidth - itemIndentWidth, termWidth + itemSeparatorWidth);
}
return term;
}
function formatList(textArray) {
return textArray.join("\n").replace(/^/gm, " ".repeat(itemIndentWidth));
}
let output = [`Usage: ${helper.commandUsage(cmd)}`, ""];
const commandDescription = helper.commandDescription(cmd);
if (commandDescription.length > 0) {
output = output.concat([helper.wrap(commandDescription, helpWidth, 0), ""]);
}
const argumentList = helper.visibleArguments(cmd).map((argument) => {
return formatItem(helper.argumentTerm(argument), helper.argumentDescription(argument));
});
if (argumentList.length > 0) {
output = output.concat(["Arguments:", formatList(argumentList), ""]);
}
const optionList = helper.visibleOptions(cmd).map((option) => {
return formatItem(helper.optionTerm(option), helper.optionDescription(option));
});
if (optionList.length > 0) {
output = output.concat(["Options:", formatList(optionList), ""]);
}
if (this.showGlobalOptions) {
const globalOptionList = helper.visibleGlobalOptions(cmd).map((option) => {
return formatItem(helper.optionTerm(option), helper.optionDescription(option));
});
if (globalOptionList.length > 0) {
output = output.concat(["Global Options:", formatList(globalOptionList), ""]);
}
}
const commandList = helper.visibleCommands(cmd).map((cmd2) => {
return formatItem(helper.subcommandTerm(cmd2), helper.subcommandDescription(cmd2));
});
if (commandList.length > 0) {
output = output.concat(["Commands:", formatList(commandList), ""]);
}
return output.join("\n");
}
/**
* Calculate the pad width from the maximum term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
padWidth(cmd, helper) {
return Math.max(
helper.longestOptionTermLength(cmd, helper),
helper.longestGlobalOptionTermLength(cmd, helper),
helper.longestSubcommandTermLength(cmd, helper),
helper.longestArgumentTermLength(cmd, helper)
);
}
/**
* Wrap the given string to width characters per line, with lines after the first indented.
* Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted.
*
* @param {string} str
* @param {number} width
* @param {number} indent
* @param {number} [minColumnWidth=40]
* @return {string}
*
*/
wrap(str, width, indent, minColumnWidth = 40) {
const indents = " \\f\\t\\v\xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF";
const manualIndent = new RegExp(`[\\n][${indents}]+`);
if (str.match(manualIndent)) return str;
const columnWidth = width - indent;
if (columnWidth < minColumnWidth) return str;
const leadingStr = str.slice(0, indent);
const columnText = str.slice(indent).replace("\r\n", "\n");
const indentString = " ".repeat(indent);
const zeroWidthSpace = "\u200B";
const breaks = `\\s${zeroWidthSpace}`;
const regex = new RegExp(`
|.{1,${columnWidth - 1}}([${breaks}]|$)|[^${breaks}]+?([${breaks}]|$)`, "g");
const lines = columnText.match(regex) || [];
return leadingStr + lines.map((line, i3) => {
if (line === "\n") return "";
return (i3 > 0 ? indentString : "") + line.trimEnd();
}).join("\n");
}
};
exports2.Help = Help2;
}
});
// node_modules/commander/lib/option.js
var require_option = __commonJS({
"node_modules/commander/lib/option.js"(exports2) {
var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
var Option2 = class {
/**
* Initialize a new `Option` with the given `flags` and `description`.
*
* @param {string} flags
* @param {string} [description]
*/
constructor(flags, description) {
this.flags = flags;
this.description = description || "";
this.required = flags.includes("<");
this.optional = flags.includes("[");
this.variadic = /\w\.\.\.[>\]]$/.test(flags);
this.mandatory = false;
const optionFlags = splitOptionFlags(flags);
this.short = optionFlags.shortFlag;
this.long = optionFlags.longFlag;
this.negate = false;
if (this.long) {
this.negate = this.long.startsWith("--no-");
}
this.defaultValue = void 0;
this.defaultValueDescription = void 0;
this.presetArg = void 0;
this.envVar = void 0;
this.parseArg = void 0;
this.hidden = false;
this.argChoices = void 0;
this.conflictsWith = [];
this.implied = void 0;
}
/**
* Set the default value, and optionally supply the description to be displayed in the help.
*
* @param {*} value
* @param {string} [description]
* @return {Option}
*/
default(value, description) {
this.defaultValue = value;
this.defaultValueDescription = description;
return this;
}
/**
* Preset to use when option used without option-argument, especially optional but also boolean and negated.
* The custom processing (parseArg) is called.
*
* @example
* new Option('--color').default('GREYSCALE').preset('RGB');
* new Option('--donate [amount]').preset('20').argParser(parseFloat);
*
* @param {*} arg
* @return {Option}
*/
preset(arg) {
this.presetArg = arg;
return this;
}
/**
* Add option name(s) that conflict with this option.
* An error will be displayed if conflicting options are found during parsing.
*
* @example
* new Option('--rgb').conflicts('cmyk');
* new Option('--js').conflicts(['ts', 'jsx']);
*
* @param {string | string[]} names
* @return {Option}
*/
conflicts(names) {
this.conflictsWith = this.conflictsWith.concat(names);
return this;
}
/**
* Specify implied option values for when this option is set and the implied options are not.
*
* The custom processing (parseArg) is not called on the implied values.
*
* @example
* program
* .addOption(new Option('--log', 'write logging information to file'))
* .addOption(new Option('--trace', 'log extra details').implies({ log: 'trace.txt' }));
*
* @param {Object} impliedOptionValues
* @return {Option}
*/
implies(impliedOptionValues) {
let newImplied = impliedOptionValues;
if (typeof impliedOptionValues === "string") {
newImplied = { [impliedOptionValues]: true };
}
this.implied = Object.assign(this.implied || {}, newImplied);
return this;
}
/**
* Set environment variable to check for option value.
*
* An environment variable is only used if when processed the current option value is
* undefined, or the source of the current value is 'default' or 'config' or 'env'.
*
* @param {string} name
* @return {Option}
*/
env(name) {
this.envVar = name;
return this;
}
/**
* Set the custom handler for processing CLI option arguments into option values.
*
* @param {Function} [fn]
* @return {Option}
*/
argParser(fn) {
this.parseArg = fn;
return this;
}
/**
* Whether the option is mandatory and must have a value after parsing.
*
* @param {boolean} [mandatory=true]
* @return {Option}
*/
makeOptionMandatory(mandatory = true) {
this.mandatory = !!mandatory;
return this;
}
/**
* Hide option in help.
*
* @param {boolean} [hide=true]
* @return {Option}
*/
hideHelp(hide = true) {
this.hidden = !!hide;
return this;
}
/**
* @api private
*/
_concatValue(value, previous) {
if (previous === this.defaultValue || !Array.isArray(previous)) {
return [value];
}
return previous.concat(value);
}
/**
* Only allow option value to be one of choices.
*
* @param {string[]} values
* @return {Option}
*/
choices(values) {
this.argChoices = values.slice();
this.parseArg = (arg, previous) => {
if (!this.argChoices.includes(arg)) {
throw new InvalidArgumentError2(`Allowed choices are ${this.argChoices.join(", ")}.`);
}
if (this.variadic) {
return this._concatValue(arg, previous);
}
return arg;
};
return this;
}
/**
* Return option name.
*
* @return {string}
*/
name() {
if (this.long) {
return this.long.replace(/^--/, "");
}
return this.short.replace(/^-/, "");
}
/**
* Return option name, in a camelcase format that can be used
* as a object attribute key.
*
* @return {string}
* @api private
*/
attributeName() {
return camelcase(this.name().replace(/^no-/, ""));
}
/**
* Check if `arg` matches the short or long flag.
*
* @param {string} arg
* @return {boolean}
* @api private
*/
is(arg) {
ret