Initial commit
This commit is contained in:
67
skills/commander-patterns/scripts/extract-command-help.sh
Executable file
67
skills/commander-patterns/scripts/extract-command-help.sh
Executable file
@@ -0,0 +1,67 @@
|
||||
#!/bin/bash
|
||||
# Extract help text from Commander.js CLI for documentation
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CLI_COMMAND="${1:?Usage: $0 <cli-command> [output-file]}"
|
||||
OUTPUT_FILE="${2:-docs/CLI-REFERENCE.md}"
|
||||
|
||||
echo "📚 Extracting help documentation from: $CLI_COMMAND"
|
||||
|
||||
# Create output directory
|
||||
mkdir -p "$(dirname "$OUTPUT_FILE")"
|
||||
|
||||
# Start markdown file
|
||||
cat > "$OUTPUT_FILE" << EOF
|
||||
# CLI Reference
|
||||
|
||||
Auto-generated documentation for $CLI_COMMAND
|
||||
|
||||
---
|
||||
|
||||
## Main Command
|
||||
|
||||
\`\`\`
|
||||
EOF
|
||||
|
||||
# Get main help
|
||||
$CLI_COMMAND --help >> "$OUTPUT_FILE" || true
|
||||
|
||||
cat >> "$OUTPUT_FILE" << 'EOF'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commands
|
||||
|
||||
EOF
|
||||
|
||||
# Extract all commands
|
||||
COMMANDS=$($CLI_COMMAND --help | grep -A 100 "Commands:" | tail -n +2 | awk '{print $1}' | grep -v "^$" || echo "")
|
||||
|
||||
if [[ -n "$COMMANDS" ]]; then
|
||||
for cmd in $COMMANDS; do
|
||||
echo "### \`$cmd\`" >> "$OUTPUT_FILE"
|
||||
echo "" >> "$OUTPUT_FILE"
|
||||
echo "\`\`\`" >> "$OUTPUT_FILE"
|
||||
$CLI_COMMAND $cmd --help >> "$OUTPUT_FILE" 2>&1 || true
|
||||
echo "\`\`\`" >> "$OUTPUT_FILE"
|
||||
echo "" >> "$OUTPUT_FILE"
|
||||
done
|
||||
else
|
||||
echo "No subcommands found." >> "$OUTPUT_FILE"
|
||||
fi
|
||||
|
||||
cat >> "$OUTPUT_FILE" << EOF
|
||||
|
||||
---
|
||||
|
||||
*Generated: $(date)*
|
||||
*CLI Version: $($CLI_COMMAND --version 2>/dev/null || echo "unknown")*
|
||||
EOF
|
||||
|
||||
echo "✅ Documentation generated: $OUTPUT_FILE"
|
||||
echo ""
|
||||
echo "Preview:"
|
||||
head -n 20 "$OUTPUT_FILE"
|
||||
echo "..."
|
||||
59
skills/commander-patterns/scripts/generate-command.sh
Executable file
59
skills/commander-patterns/scripts/generate-command.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
# Generate Commander.js command with options and arguments
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
COMMAND_NAME="${1:?Usage: $0 <command-name> [output-file]}"
|
||||
OUTPUT_FILE="${2:-src/commands/${COMMAND_NAME}.ts}"
|
||||
|
||||
# Create output directory
|
||||
mkdir -p "$(dirname "$OUTPUT_FILE")"
|
||||
|
||||
echo "📝 Generating Commander.js command: $COMMAND_NAME"
|
||||
|
||||
cat > "$OUTPUT_FILE" << 'EOF'
|
||||
import { Command, Option } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
export function create{{COMMAND_NAME_PASCAL}}Command(): Command {
|
||||
const command = new Command('{{COMMAND_NAME}}')
|
||||
.description('{{DESCRIPTION}}')
|
||||
.option('-v, --verbose', 'verbose output', false)
|
||||
.addOption(
|
||||
new Option('-e, --environment <env>', 'target environment')
|
||||
.choices(['dev', 'staging', 'prod'])
|
||||
.default('dev')
|
||||
)
|
||||
.action(async (options) => {
|
||||
try {
|
||||
console.log(chalk.blue(`Running {{COMMAND_NAME}} command...`));
|
||||
console.log('Options:', options);
|
||||
|
||||
// TODO: Implement command logic
|
||||
|
||||
console.log(chalk.green('✓ Command completed successfully'));
|
||||
} catch (error) {
|
||||
console.error(chalk.red('✗ Command failed:'), error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
return command;
|
||||
}
|
||||
EOF
|
||||
|
||||
# Convert command name to PascalCase
|
||||
COMMAND_NAME_PASCAL=$(echo "$COMMAND_NAME" | sed -r 's/(^|-)([a-z])/\U\2/g')
|
||||
|
||||
# Replace placeholders
|
||||
sed -i "s/{{COMMAND_NAME}}/$COMMAND_NAME/g" "$OUTPUT_FILE"
|
||||
sed -i "s/{{COMMAND_NAME_PASCAL}}/$COMMAND_NAME_PASCAL/g" "$OUTPUT_FILE"
|
||||
sed -i "s/{{DESCRIPTION}}/Execute $COMMAND_NAME operation/g" "$OUTPUT_FILE"
|
||||
|
||||
echo "✅ Command generated: $OUTPUT_FILE"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Implement command logic in the action handler"
|
||||
echo " 2. Add custom options and arguments as needed"
|
||||
echo " 3. Import and add to main CLI: program.addCommand(create${COMMAND_NAME_PASCAL}Command())"
|
||||
echo ""
|
||||
75
skills/commander-patterns/scripts/generate-subcommand.sh
Executable file
75
skills/commander-patterns/scripts/generate-subcommand.sh
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/bin/bash
|
||||
# Generate nested subcommand structure for Commander.js
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PARENT_COMMAND="${1:?Usage: $0 <parent-command> <subcommand> [output-file]}"
|
||||
SUBCOMMAND="${2:?Usage: $0 <parent-command> <subcommand> [output-file]}"
|
||||
OUTPUT_FILE="${3:-src/commands/${PARENT_COMMAND}/${SUBCOMMAND}.ts}"
|
||||
|
||||
# Create output directory
|
||||
mkdir -p "$(dirname "$OUTPUT_FILE")"
|
||||
|
||||
echo "📝 Generating subcommand: $PARENT_COMMAND $SUBCOMMAND"
|
||||
|
||||
cat > "$OUTPUT_FILE" << 'EOF'
|
||||
import { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
export function create{{SUBCOMMAND_PASCAL}}Command(): Command {
|
||||
const command = new Command('{{SUBCOMMAND}}')
|
||||
.description('{{DESCRIPTION}}')
|
||||
.action(async (options) => {
|
||||
try {
|
||||
console.log(chalk.blue(`Running {{PARENT_COMMAND}} {{SUBCOMMAND}}...`));
|
||||
|
||||
// TODO: Implement subcommand logic
|
||||
|
||||
console.log(chalk.green('✓ Subcommand completed'));
|
||||
} catch (error) {
|
||||
console.error(chalk.red('✗ Subcommand failed:'), error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
return command;
|
||||
}
|
||||
EOF
|
||||
|
||||
# Convert to PascalCase
|
||||
SUBCOMMAND_PASCAL=$(echo "$SUBCOMMAND" | sed -r 's/(^|-)([a-z])/\U\2/g')
|
||||
|
||||
# Replace placeholders
|
||||
sed -i "s/{{PARENT_COMMAND}}/$PARENT_COMMAND/g" "$OUTPUT_FILE"
|
||||
sed -i "s/{{SUBCOMMAND}}/$SUBCOMMAND/g" "$OUTPUT_FILE"
|
||||
sed -i "s/{{SUBCOMMAND_PASCAL}}/$SUBCOMMAND_PASCAL/g" "$OUTPUT_FILE"
|
||||
sed -i "s/{{DESCRIPTION}}/$SUBCOMMAND operation for $PARENT_COMMAND/g" "$OUTPUT_FILE"
|
||||
|
||||
# Generate parent command file if it doesn't exist
|
||||
PARENT_FILE="src/commands/${PARENT_COMMAND}/index.ts"
|
||||
if [[ ! -f "$PARENT_FILE" ]]; then
|
||||
mkdir -p "$(dirname "$PARENT_FILE")"
|
||||
cat > "$PARENT_FILE" << EOF
|
||||
import { Command } from 'commander';
|
||||
import { create${SUBCOMMAND_PASCAL}Command } from './${SUBCOMMAND}';
|
||||
|
||||
export function create${PARENT_COMMAND^}Command(): Command {
|
||||
const command = new Command('${PARENT_COMMAND}')
|
||||
.description('${PARENT_COMMAND^} operations');
|
||||
|
||||
// Add subcommands
|
||||
command.addCommand(create${SUBCOMMAND_PASCAL}Command());
|
||||
|
||||
return command;
|
||||
}
|
||||
EOF
|
||||
echo "✅ Created parent command: $PARENT_FILE"
|
||||
fi
|
||||
|
||||
echo "✅ Subcommand generated: $OUTPUT_FILE"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Implement subcommand logic"
|
||||
echo " 2. Add to parent command: ${PARENT_FILE}"
|
||||
echo " 3. Import parent in main CLI: program.addCommand(create${PARENT_COMMAND^}Command())"
|
||||
echo ""
|
||||
83
skills/commander-patterns/scripts/test-commander-cli.sh
Executable file
83
skills/commander-patterns/scripts/test-commander-cli.sh
Executable file
@@ -0,0 +1,83 @@
|
||||
#!/bin/bash
|
||||
# Test Commander.js CLI with various inputs
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CLI_COMMAND="${1:?Usage: $0 <cli-command> [test-suite]}"
|
||||
TEST_SUITE="${2:-basic}"
|
||||
|
||||
echo "🧪 Testing Commander.js CLI: $CLI_COMMAND"
|
||||
echo "Test suite: $TEST_SUITE"
|
||||
echo ""
|
||||
|
||||
PASSED=0
|
||||
FAILED=0
|
||||
|
||||
# Helper function to run test
|
||||
run_test() {
|
||||
local test_name="$1"
|
||||
local test_command="$2"
|
||||
local expected_exit_code="${3:-0}"
|
||||
|
||||
echo -n "Testing: $test_name ... "
|
||||
|
||||
if eval "$test_command" > /dev/null 2>&1; then
|
||||
actual_exit_code=0
|
||||
else
|
||||
actual_exit_code=$?
|
||||
fi
|
||||
|
||||
if [[ $actual_exit_code -eq $expected_exit_code ]]; then
|
||||
echo "✅ PASS"
|
||||
((PASSED++))
|
||||
else
|
||||
echo "❌ FAIL (expected exit code $expected_exit_code, got $actual_exit_code)"
|
||||
((FAILED++))
|
||||
fi
|
||||
}
|
||||
|
||||
# Basic tests
|
||||
if [[ "$TEST_SUITE" == "basic" || "$TEST_SUITE" == "all" ]]; then
|
||||
echo "Running basic tests..."
|
||||
run_test "Help flag" "$CLI_COMMAND --help" 0
|
||||
run_test "Version flag" "$CLI_COMMAND --version" 0
|
||||
run_test "No arguments" "$CLI_COMMAND" 1
|
||||
fi
|
||||
|
||||
# Command tests
|
||||
if [[ "$TEST_SUITE" == "commands" || "$TEST_SUITE" == "all" ]]; then
|
||||
echo ""
|
||||
echo "Running command tests..."
|
||||
run_test "List commands" "$CLI_COMMAND --help | grep -q 'Commands:'" 0
|
||||
fi
|
||||
|
||||
# Option tests
|
||||
if [[ "$TEST_SUITE" == "options" || "$TEST_SUITE" == "all" ]]; then
|
||||
echo ""
|
||||
echo "Running option tests..."
|
||||
run_test "Unknown option" "$CLI_COMMAND --unknown-option" 1
|
||||
run_test "Short flag" "$CLI_COMMAND -v" 0
|
||||
run_test "Long flag" "$CLI_COMMAND --verbose" 0
|
||||
fi
|
||||
|
||||
# Argument tests
|
||||
if [[ "$TEST_SUITE" == "arguments" || "$TEST_SUITE" == "all" ]]; then
|
||||
echo ""
|
||||
echo "Running argument tests..."
|
||||
run_test "Required argument missing" "$CLI_COMMAND deploy" 1
|
||||
run_test "Required argument provided" "$CLI_COMMAND deploy production" 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Test Results:"
|
||||
echo " Passed: $PASSED"
|
||||
echo " Failed: $FAILED"
|
||||
echo ""
|
||||
|
||||
if [[ $FAILED -gt 0 ]]; then
|
||||
echo "❌ Some tests failed"
|
||||
exit 1
|
||||
else
|
||||
echo "✅ All tests passed!"
|
||||
exit 0
|
||||
fi
|
||||
139
skills/commander-patterns/scripts/validate-commander-structure.sh
Executable file
139
skills/commander-patterns/scripts/validate-commander-structure.sh
Executable file
@@ -0,0 +1,139 @@
|
||||
#!/bin/bash
|
||||
# Validate Commander.js CLI structure and patterns
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CLI_FILE="${1:?Usage: $0 <cli-file.ts|js>}"
|
||||
|
||||
if [[ ! -f "$CLI_FILE" ]]; then
|
||||
echo "Error: File not found: $CLI_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🔍 Validating Commander.js CLI structure: $CLI_FILE"
|
||||
echo ""
|
||||
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
|
||||
# Check for Commander import
|
||||
if grep -q "from 'commander'" "$CLI_FILE" || grep -q 'require("commander")' "$CLI_FILE"; then
|
||||
echo "✅ Commander.js import found"
|
||||
else
|
||||
echo "❌ Missing Commander.js import"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check for Command instantiation
|
||||
if grep -q "new Command()" "$CLI_FILE" || grep -q "= program" "$CLI_FILE"; then
|
||||
echo "✅ Command instance created"
|
||||
else
|
||||
echo "❌ No Command instance found"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check for program.parse()
|
||||
if grep -q "\.parse()" "$CLI_FILE"; then
|
||||
echo "✅ program.parse() called"
|
||||
else
|
||||
echo "❌ Missing program.parse() call"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check for .name()
|
||||
if grep -q "\.name(" "$CLI_FILE"; then
|
||||
echo "✅ CLI name defined"
|
||||
else
|
||||
echo "⚠️ CLI name not set (recommended)"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for .description()
|
||||
if grep -q "\.description(" "$CLI_FILE"; then
|
||||
echo "✅ CLI description defined"
|
||||
else
|
||||
echo "⚠️ CLI description not set (recommended)"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for .version()
|
||||
if grep -q "\.version(" "$CLI_FILE"; then
|
||||
echo "✅ CLI version defined"
|
||||
else
|
||||
echo "⚠️ CLI version not set (recommended)"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for commands
|
||||
COMMAND_COUNT=$(grep -c "\.command(" "$CLI_FILE" || echo "0")
|
||||
if [[ $COMMAND_COUNT -gt 0 ]]; then
|
||||
echo "✅ Found $COMMAND_COUNT command(s)"
|
||||
else
|
||||
echo "⚠️ No commands defined"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for action handlers
|
||||
ACTION_COUNT=$(grep -c "\.action(" "$CLI_FILE" || echo "0")
|
||||
if [[ $ACTION_COUNT -gt 0 ]]; then
|
||||
echo "✅ Found $ACTION_COUNT action handler(s)"
|
||||
else
|
||||
echo "⚠️ No action handlers defined"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for options
|
||||
OPTION_COUNT=$(grep -c "\.option(" "$CLI_FILE" || echo "0")
|
||||
ADDOPTION_COUNT=$(grep -c "\.addOption(" "$CLI_FILE" || echo "0")
|
||||
TOTAL_OPTIONS=$((OPTION_COUNT + ADDOPTION_COUNT))
|
||||
if [[ $TOTAL_OPTIONS -gt 0 ]]; then
|
||||
echo "✅ Found $TOTAL_OPTIONS option(s)"
|
||||
else
|
||||
echo "⚠️ No options defined"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for arguments
|
||||
ARGUMENT_COUNT=$(grep -c "\.argument(" "$CLI_FILE" || echo "0")
|
||||
if [[ $ARGUMENT_COUNT -gt 0 ]]; then
|
||||
echo "✅ Found $ARGUMENT_COUNT argument(s)"
|
||||
fi
|
||||
|
||||
# Check for Option class usage
|
||||
if grep -q "new Option(" "$CLI_FILE"; then
|
||||
echo "✅ Option class used (advanced)"
|
||||
fi
|
||||
|
||||
# Check for error handling
|
||||
if grep -q "try\|catch" "$CLI_FILE" || grep -q "\.exitOverride()" "$CLI_FILE"; then
|
||||
echo "✅ Error handling present"
|
||||
else
|
||||
echo "⚠️ No error handling detected (recommended)"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for TypeScript
|
||||
if [[ "$CLI_FILE" == *.ts ]]; then
|
||||
echo "✅ TypeScript file"
|
||||
# Check for types
|
||||
if grep -q "import.*Command.*Option.*from 'commander'" "$CLI_FILE"; then
|
||||
echo "✅ Proper TypeScript imports"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Summary:"
|
||||
echo " Errors: $ERRORS"
|
||||
echo " Warnings: $WARNINGS"
|
||||
echo ""
|
||||
|
||||
if [[ $ERRORS -gt 0 ]]; then
|
||||
echo "❌ Validation failed with $ERRORS error(s)"
|
||||
exit 1
|
||||
elif [[ $WARNINGS -gt 0 ]]; then
|
||||
echo "⚠️ Validation passed with $WARNINGS warning(s)"
|
||||
exit 0
|
||||
else
|
||||
echo "✅ Validation passed - excellent CLI structure!"
|
||||
exit 0
|
||||
fi
|
||||
Reference in New Issue
Block a user