@guyycodes/plugin-sdk
Version:
AI-powered plugin scaffolding tool - Create full-stack applications with 7+ AI models, 50+ business integrations, and production-ready infrastructure
353 lines (297 loc) ⢠12.5 kB
JavaScript
// finalCli.js
// This is the CLI for scaffolding new plugin projects with the Plugin SDK.
const { Command } = require('commander');
const inquirer = require('inquirer');
const fs = require('fs-extra');
const path = require('path');
const { execSync } = require('child_process');
const chalk = require('chalk');
const ora = require('ora');
// Import abstracted modules
const { createBackendFiles } = require('./createBackend');
const { createGitHubWorkflow } = require('./githubWorkflow');
const { scaffoldViteClient } = require('./viteClient');
const { createAppConfigNode, createAppConfigPython, createDockerIgnore, createGitIgnore, createReadmeMd } = require('./appConfig');
const program = new Command();
// CLI setup
program
.name('plugin-sdk')
.description('Plugin SDK - Create new plugin projects')
.version('1.0.0');
// ============================================================================
// INIT COMMAND - Create new plugin project
// ============================================================================
program
.command('init')
.argument('[project-name]', 'Plugin project name')
.option('--project-name <name>', 'Plugin project name')
.option('--backend <type>', 'Backend type (nodejs|python)', 'nodejs')
.option('--frontend <type>', 'Frontend type (typescript|javascript)', 'typescript')
.option('--integration <service>', 'Integration service (quickbooks|calendar|stripe|mailchimp|custom)', 'custom')
.description('Initialize a new plugin project')
.action(async (projectNameArg, options) => {
const projectName = projectNameArg || options.projectName;
if (!projectName) {
const answers = await inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: 'What is your plugin project name?',
validate: (input) => {
if (!input.trim()) return 'Project name is required';
if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(input)) {
return 'Project name must start with a letter and contain only letters, numbers, and hyphens';
}
return true;
}
},
{
type: 'list',
name: 'backendType',
message: 'Choose your backend technology:',
choices: [
{ name: 'Node.js (Express- API ONLY)', value: 'nodejs' },
{ name: 'Python (FastAPI- API + ML/LLM)', value: 'python' }
]
},
{
type: 'list',
name: 'frontendType',
message: 'Choose your frontend type:',
choices: [
{ name: 'TypeScript (Recommended)', value: 'typescript' },
{ name: 'JavaScript', value: 'javascript' }
]
},
{
type: 'list',
name: 'integration',
message: 'What type of plugin are you creating?',
choices: [
{ name: 'Custom Plugin (Blank template for your own integration)', value: 'custom' },
// { name: 'QuickBooks (Pre-built accounting integration)', value: 'quickbooks' },
// { name: 'Google Calendar (Pre-built scheduling integration)', value: 'calendar' },
// { name: 'Stripe (Pre-built payments integration)', value: 'stripe' },
// { name: 'Mailchimp (Pre-built email marketing integration)', value: 'mailchimp' }
]
}
]);
// Use answers when no project name provided
await initProject(answers.projectName, answers.backendType, answers.frontendType, answers.integration);
} else {
// Use the provided project name and options
await initProject(projectName, options.backend, options.frontend, options.integration);
}
});
// ============================================================================
// IMPLEMENTATION FUNCTIONS
// ============================================================================
async function initProject(projectName, backendType, frontendType, integration) {
console.log(chalk.blue(`š Creating plugin project: ${projectName}`));
console.log(chalk.gray(` Backend: ${backendType}`));
console.log(chalk.gray(` Frontend: ${frontendType}`));
console.log(chalk.gray(` Integration: ${integration}`));
const projectPath = path.resolve(process.cwd(), projectName);
if (fs.existsSync(projectPath)) {
console.error(chalk.red(`Error: Directory ${projectName} already exists`));
process.exit(1);
}
const spinner = ora('Setting up project structure...').start();
try {
// Create project directory and navigate
fs.ensureDirSync(projectPath);
process.chdir(projectPath);
// Create directory structure
await createDirectoryStructure(projectPath);
spinner.text = 'Creating directory structure...';
// Scaffold Vite client with Module Federation
spinner.text = 'Scaffolding Vite client...';
await scaffoldViteClient(projectPath, projectName, frontendType, integration, backendType);
// Create backend files
spinner.text = `Creating ${backendType} backend...this might take a second or two...`;
await createBackendFiles(projectPath, backendType, integration, projectName);
// Create configuration files
spinner.text = 'Creating configuration files...';
await createConfigFiles(projectPath, projectName, backendType, frontendType, integration);
// Create appConfig.json
spinner.text = 'Creating appConfig.json...';
if (backendType === 'nodejs') {
await createAppConfigNode(projectPath, projectName, backendType, integration);
} else {
await createAppConfigPython(projectPath, projectName, backendType, integration);
}
await createDockerIgnore(projectPath, projectName);
await createGitIgnore(projectPath, projectName);
await createReadmeMd(projectPath, projectName);
// Create GitHub Actions workflow
spinner.text = 'Creating CI/CD workflow...';
await createGitHubWorkflow(projectPath, projectName, backendType);
// Initialize git repository
spinner.text = 'Initializing git repository...';
await initializeGit(projectPath);
// Install dependencies
spinner.text = 'Installing dependencies...';
await installDependencies(projectPath, backendType);
spinner.succeed(chalk.green(`ā
Successfully created ${projectName}!`));
// Show next steps
console.log(chalk.yellow('\nš Next steps:'));
console.log(` 1. ${chalk.cyan(`cd ${projectName}`)}`);
console.log(` 2. ${chalk.cyan('Setup Env Variables')} # See README.md for required variables`);
console.log(` 3. ${chalk.cyan('npm install')} # Install dependencies in root - control servers from root`);
console.log(` 4. ${chalk.cyan('npm run install:server')} # Install dependencies in backend server`);
console.log(` 5. ${chalk.cyan('npm run install:client')} # Install dependencies in frontend server`);
console.log(` 6. ${chalk.cyan('npm run dev')} # Start both servers (w/concurrently from root)`);
console.log(` 7. ${chalk.cyan('npm run dev:server')} # Start backend server`);
console.log(` 8. ${chalk.cyan('npm run dev:client')} # Start frontend server`);
console.log(` 9. ${chalk.cyan('Api keys included in /config/auth files')}`);
console.log(chalk.yellow('\nš§ Configuration:'));
console.log(` ⢠Customize ${chalk.cyan('src/client/src/PluginApp')} for your UI`);
console.log(` ⢠Backend runs on ${chalk.cyan('http://localhost:3000')}, Frontend on ${chalk.cyan('http://localhost:5173')}`);
console.log(` ⢠API calls use ${chalk.cyan('/api')} prefix (auto-routed in development)`);
console.log(` ⢠Update ${chalk.cyan('manifest.json')} with deployment URLs after deploy`);
console.log(` ⢠See ${chalk.cyan('Python implementation has live thought streaming + full ML/LLM Templates')}`);
console.log(` ⢠See ${chalk.cyan('Typescript implementation is limited to API only (no ML/LLM Templates)')}`);
console.log(chalk.yellow('\nš API Routing:'));
console.log(` ⢠Development: ${chalk.cyan('/api/health')} ā Vite proxy ā ${chalk.cyan('localhost:3000/health')}`);
console.log(` ⢠Production: ${chalk.cyan('/api/integrations/${projectName}/health')} ā Main app ā Plugin backend`);
} catch (error) {
spinner.fail('Failed to create project');
console.error(chalk.red('Error:'), error.message);
if (fs.existsSync(projectPath)) {
fs.removeSync(projectPath);
}
process.exit(1);
}
}
// ============================================================================
// HELPER FUNCTIONS
// ============================================================================
async function createDirectoryStructure(projectPath) {
const dirs = [
'src/client',
'src/server',
'.github/workflows',
];
dirs.forEach(dir => {
fs.ensureDirSync(path.join(projectPath, dir));
});
}
async function createConfigFiles(projectPath, projectName, backendType, frontendType, integration) {
// Root package.json
const rootPackageJson = {
"name": projectName,
"version": "1.0.0",
"description": integration === 'custom' ? `Custom plugin` : `${integration} integration plugin`,
"scripts": {
"dev:client": "cd src/client && npm run dev",
"dev:server": backendType === 'nodejs' ? "cd src/server && npm run dev" : "cd src/server && source venv/bin/activate && python main.py",
"build:client": "cd src/client && npm run build",
"venv": backendType === 'nodejs' ? "echo 'Node.js backend, no venv needed'" : "cd src/server && python3.11 -m venv venv && source venv/bin/activate",
"install-uv": backendType === 'nodejs' ? "echo 'Node.js backend, no venv needed'" : "curl -LsSf https://astral.sh/uv/install.sh | sh",
"install:client": "cd src/client && npm install",
"install:server": backendType === 'nodejs' ? "cd src/server && npm install" : "npm run venv && cd src/server && source venv/bin/activate && pip install -r requirements.txt",
"build:server": backendType === 'nodejs' ? "cd src/server && npm run build" : "echo 'Python server ready'",
"dev": backendType === 'nodejs' ? "concurrently \"cd src/server && npm run dev\" \"cd src/client && npm run dev\"" : "concurrently \"cd src/client && npm run dev\" \"cd src/server && source venv/bin/activate && python main.py\"",
"test:connection": "curl -s http://localhost:3000/health | head -n 1 || echo 'ā Backend not running on port 3000'"
},
"devDependencies": {
"concurrently": "^9.1.2"
},
"keywords": ["plugin", integration],
"license": "Apache-2.0"
};
fs.writeJsonSync(path.join(projectPath, 'package.json'), rootPackageJson, { spaces: 2 });
}
async function initializeGit(projectPath) {
try {
execSync('git init', { cwd: projectPath, stdio: 'pipe' });
const gitignore = `# Dependencies
# Dependencies
node_modules/
# Environment variables
.env
.env.local
# Build outputs
dist/
build/
# IDE files
.vscode/
.idea/
*.swp
# OS files
.DS_Store
Thumbs.db
# Logs
*.log
# Model weights and large files - UPDATED PATHS
src/server/models/*/model/
src/server/models/*/*/model/
src/server/models/*/model/images_output/
src/server/models/*/images_output/
*.safetensors
*.bin
*.pt
*.pth
*.onnx
*.h5
*.keras
# Virtual environment
src/server/venv/
venv/
ENV/
env/
venv
# Jupyter notebooks
.ipynb_checkpoints/
# pytest
.pytest_cache/
# Coverage reports
htmlcov/
.coverage
.coverage.*
coverage.xml
*.cover
# Python
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
`;
fs.writeFileSync(path.join(projectPath, '.gitignore'), gitignore);
execSync('git add .', { cwd: projectPath, stdio: 'pipe' });
execSync('git commit -m "Initial commit: Plugin scaffolding"', { cwd: projectPath, stdio: 'pipe' });
} catch (error) {
console.warn(chalk.yellow('Warning: Could not initialize git repository'));
}
}
async function installDependencies(projectPath, backendType) {
// Install client dependencies
execSync('npm install', {
cwd: path.join(projectPath, 'src/client'),
stdio: 'inherit'
});
if (backendType === 'nodejs') {
execSync('npm install', {
cwd: path.join(projectPath, 'src/server'),
stdio: 'inherit'
});
}
}
// Parse command line arguments
program.parse();