@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
429 lines (417 loc) • 15.7 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.CompletionSystem = void 0;
exports.createCompletionSystem = createCompletionSystem;
exports.getGlobalCompletion = getGlobalCompletion;
exports.setGlobalCompletion = setGlobalCompletion;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
class CompletionSystem {
constructor(config = {}) {
this.commands = new Map();
this.customProviders = new Map();
this.config = {
enableFileCompletion: true,
enableDirectoryCompletion: true,
enableCommandCompletion: true,
enableOptionCompletion: true,
enableValueCompletion: true,
customCompletions: {},
...config
};
this.setupBuiltinProviders();
}
setupBuiltinProviders() {
// Framework completion
this.customProviders.set('framework', () => [
'react', 'vue', 'svelte', 'angular', 'vanilla'
]);
// Package manager completion
this.customProviders.set('packageManager', () => [
'npm', 'yarn', 'pnpm', 'bun'
]);
// Template completion
this.customProviders.set('template', () => [
'basic', 'ecommerce', 'dashboard', 'saas', 'blog'
]);
// Log level completion
this.customProviders.set('logLevel', () => [
'debug', 'info', 'warn', 'error', 'silent'
]);
// Boolean completion
this.customProviders.set('boolean', () => [
'true', 'false'
]);
// Environment completion
this.customProviders.set('environment', () => [
'development', 'staging', 'production', 'test'
]);
// Port completion (common ports)
this.customProviders.set('port', () => [
'3000', '3001', '3002', '4000', '5000', '8000', '8080', '9000'
]);
}
registerCommand(command) {
this.commands.set(command.name, command);
// Register aliases
if (command.alias) {
for (const alias of command.alias) {
this.commands.set(alias, command);
}
}
}
unregisterCommand(name) {
const command = this.commands.get(name);
if (command) {
this.commands.delete(name);
// Remove aliases
if (command.alias) {
for (const alias of command.alias) {
this.commands.delete(alias);
}
}
}
}
registerProvider(name, provider) {
this.customProviders.set(name, provider);
}
async complete(args, current, context) {
const completions = [];
// Complete command names
if (args.length === 0 || (args.length === 1 && !current.startsWith('-'))) {
if (this.config.enableCommandCompletion) {
const commandCompletions = this.completeCommands(current);
completions.push(...commandCompletions);
}
return completions;
}
const commandName = args[0];
const command = this.commands.get(commandName);
if (!command) {
return completions;
}
// Complete options
if (current.startsWith('-')) {
if (this.config.enableOptionCompletion) {
const optionCompletions = this.completeOptions(command, current);
completions.push(...optionCompletions);
}
return completions;
}
// Complete option values
const lastArg = args[args.length - 1];
if (lastArg && lastArg.startsWith('-')) {
if (this.config.enableValueCompletion) {
const valueCompletions = await this.completeOptionValues(command, lastArg, current, context);
completions.push(...valueCompletions);
}
return completions;
}
// Complete arguments
const argumentCompletions = await this.completeArguments(command, args.slice(1), current, context);
completions.push(...argumentCompletions);
// Complete files and directories
if (this.config.enableFileCompletion || this.config.enableDirectoryCompletion) {
const pathCompletions = await this.completeFiles(current, context);
completions.push(...pathCompletions);
}
return Array.from(new Set(completions)).sort();
}
completeCommands(current) {
const commandNames = Array.from(this.commands.keys());
return commandNames.filter(name => name.startsWith(current) &&
!this.commands.get(name)?.hidden);
}
completeOptions(command, current) {
if (!command.options)
return [];
const completions = [];
for (const option of command.options) {
const flags = option.flag.split(',').map(f => f.trim());
for (const flag of flags) {
if (flag.startsWith(current)) {
completions.push(flag);
}
}
}
return completions;
}
async completeOptionValues(command, option, current, context) {
if (!command.options)
return [];
// Find the option definition
const optionDef = command.options.find(opt => {
const flags = opt.flag.split(',').map(f => f.trim());
return flags.includes(option);
});
if (!optionDef)
return [];
// Use choices if available
if (optionDef.choices) {
return optionDef.choices
.filter(choice => choice.toString().startsWith(current))
.map(choice => choice.toString());
}
// Use custom completion provider if available
const providerName = this.getProviderForOption(option);
if (providerName && this.customProviders.has(providerName)) {
const provider = this.customProviders.get(providerName);
const results = await provider(current, [option], context);
return results.filter(result => result.startsWith(current));
}
return [];
}
getProviderForOption(option) {
const providerMap = {
'--framework': 'framework',
'-f': 'framework',
'--package-manager': 'packageManager',
'--pm': 'packageManager',
'--template': 'template',
'-t': 'template',
'--log-level': 'logLevel',
'--yes': 'boolean',
'--no': 'boolean',
'--typescript': 'boolean',
'--ts': 'boolean',
'--port': 'port',
'-p': 'port',
'--env': 'environment',
'--environment': 'environment'
};
return providerMap[option] || null;
}
async completeArguments(command, args, current, context) {
if (!command.arguments)
return [];
const argIndex = args.length;
const argumentDef = command.arguments[argIndex];
if (!argumentDef)
return [];
// Use custom completion if available
const providerName = this.getProviderForArgument(command.name, argumentDef.name);
if (providerName && this.customProviders.has(providerName)) {
const provider = this.customProviders.get(providerName);
return await provider(current, args, context);
}
return [];
}
getProviderForArgument(command, argument) {
const providerMap = {
init: {
name: 'projectName'
},
add: {
name: 'microfrontendName'
}
};
return providerMap[command]?.[argument] || null;
}
async completeFiles(current, context) {
try {
const isDirectory = current.endsWith('/');
const basePath = isDirectory ? current : path.dirname(current);
const filename = isDirectory ? '' : path.basename(current);
const fullPath = path.resolve(context.cwd, basePath);
if (!await fs.pathExists(fullPath)) {
return [];
}
const entries = await fs.readdir(fullPath, { withFileTypes: true });
const completions = [];
for (const entry of entries) {
if (!entry.name.startsWith(filename))
continue;
if (entry.isDirectory() && this.config.enableDirectoryCompletion) {
const dirPath = path.join(basePath, entry.name);
completions.push(dirPath + '/');
}
else if (entry.isFile() && this.config.enableFileCompletion) {
const filePath = path.join(basePath, entry.name);
completions.push(filePath);
}
}
return completions;
}
catch {
return [];
}
}
generateShellCompletions(programName) {
return [
this.generateBashCompletion(programName),
this.generateZshCompletion(programName),
this.generateFishCompletion(programName),
this.generatePowerShellCompletion(programName)
];
}
generateBashCompletion(programName) {
const script = `#!/bin/bash
_${programName}_completions() {
local cur prev words cword
_init_completion || return
# Call the completion endpoint
local completions
completions=$(${programName} __complete "\${COMP_WORDS[@]}" "\${COMP_CWORD}")
if [[ $? -eq 0 ]]; then
COMPREPLY=($(compgen -W "\${completions}" -- "\${cur}"))
fi
}
complete -F _${programName}_completions ${programName}`;
return {
shell: 'bash',
script,
installPath: '/etc/bash_completion.d/' + programName,
instructions: `To install bash completion:
1. Save the script to /etc/bash_completion.d/${programName}
2. Or add to your ~/.bashrc:
source <(${programName} completion bash)
3. Restart your shell or run: source ~/.bashrc`
};
}
generateZshCompletion(programName) {
const script = `#compdef ${programName}
_${programName}() {
local context state line
local -a completions
# Call the completion endpoint
completions=($(${programName} __complete "\${words[@]}" "\${CURRENT}"))
if [[ $? -eq 0 ]]; then
_describe 'commands' completions
fi
}
_${programName} "$@"`;
return {
shell: 'zsh',
script,
installPath: `${os.homedir()}/.zsh/completions/_${programName}`,
instructions: `To install zsh completion:
1. Create directory: mkdir -p ~/.zsh/completions
2. Save the script to ~/.zsh/completions/_${programName}
3. Add to your ~/.zshrc:
fpath=(~/.zsh/completions $fpath)
autoload -U compinit && compinit
4. Restart your shell`
};
}
generateFishCompletion(programName) {
const script = `function __${programName}_complete
set -l completions (${programName} __complete (commandline -cp) (commandline -t))
if test $status -eq 0
printf '%s\\n' $completions
end
end
complete -c ${programName} -f -a "(__${programName}_complete)"`;
return {
shell: 'fish',
script,
installPath: `${os.homedir()}/.config/fish/completions/${programName}.fish`,
instructions: `To install fish completion:
1. Create directory: mkdir -p ~/.config/fish/completions
2. Save the script to ~/.config/fish/completions/${programName}.fish
3. Restart your shell`
};
}
generatePowerShellCompletion(programName) {
const script = `Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
$completions = & ${programName} __complete $commandAst.CommandElements
$completions | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
}`;
return {
shell: 'powershell',
script,
installPath: '$PROFILE',
instructions: `To install PowerShell completion:
1. Add the script to your PowerShell profile
2. Or run: ${programName} completion powershell | Out-String | Invoke-Expression
3. Restart PowerShell`
};
}
async installCompletion(shell, programName) {
const completions = this.generateShellCompletions(programName);
const completion = completions.find(c => c.shell === shell);
if (!completion) {
throw new Error(`Unsupported shell: ${shell}`);
}
try {
const installDir = path.dirname(completion.installPath);
await fs.ensureDir(installDir);
await fs.writeFile(completion.installPath, completion.script);
console.log(`✓ Completion installed to ${completion.installPath}`);
console.log('\nNext steps:');
console.log(completion.instructions);
}
catch (error) {
throw new Error(`Failed to install completion: ${error.message}`);
}
}
async handleCompletionRequest(args) {
// Parse completion request
const words = args.slice(0, -1); // All words except the current one
const current = args[args.length - 1] || '';
const context = {
command: words[0] || '',
cwd: process.cwd(),
env: process.env
};
try {
const completions = await this.complete(words.slice(1), current, context);
console.log(completions.join('\n'));
}
catch (error) {
// Silent fail for completion errors
process.exit(1);
}
}
}
exports.CompletionSystem = CompletionSystem;
// Global completion system
let globalCompletion = null;
function createCompletionSystem(config) {
return new CompletionSystem(config);
}
function getGlobalCompletion() {
if (!globalCompletion) {
globalCompletion = new CompletionSystem();
}
return globalCompletion;
}
function setGlobalCompletion(completion) {
globalCompletion = completion;
}