UNPKG

pame-core-cli

Version:

PAME.AI Core Operating System CLI - Open Source AI Platform for Agentic Commerce

362 lines โ€ข 14.2 kB
import { Command } from 'commander'; import chalk from 'chalk'; import ora from 'ora'; import { TutorialSDK } from '../sdk/tutorial-sdk.js'; import * as path from 'path'; // Simple implementations for inquirer prompts since @inquirer/prompts may not be available async function select(options) { console.log(options.message); options.choices.forEach((choice, i) => { console.log(`${i + 1}. ${choice.name}`); }); // In a real implementation, this would collect user input return options.choices[0].value; } async function input(options) { console.log(options.message); if (options.default) { console.log(`Default: ${options.default}`); } // In a real implementation, this would collect user input return options.default || ''; } async function confirm(options) { console.log(`${options.message} (${options.default ? 'Y/n' : 'y/N'})`); // In a real implementation, this would collect user input return options.default || false; } export const tutorialCommand = new Command('tutorial') .description('Create and deploy guided tutorials for any feature or process') .addCommand(new Command('create') .description('Create a new tutorial') .option('-t, --template <type>', 'Use a template (feature-onboarding, api-integration, debugging-guide, best-practices)') .action(async (options) => { await createTutorial(options); })) .addCommand(new Command('deploy') .description('Deploy a tutorial to a project') .argument('<tutorialId>', 'Tutorial ID to deploy') .argument('<projectPath>', 'Target project path') .option('--auto-start', 'Auto-start the tutorial after deployment') .option('--target-file <file>', 'Inject tutorial prompt into specific file') .action(async (tutorialId, projectPath, options) => { await deployTutorial(tutorialId, projectPath, options); })) .addCommand(new Command('list') .description('List all available tutorials') .action(async () => { await listTutorials(); })) .addCommand(new Command('analytics') .description('View tutorial analytics') .argument('<tutorialId>', 'Tutorial ID') .action(async (tutorialId) => { await showAnalytics(tutorialId); })) .addCommand(new Command('test') .description('Test a tutorial locally') .argument('<tutorialId>', 'Tutorial ID to test') .action(async (tutorialId) => { await testTutorial(tutorialId); })); async function createTutorial(options) { const sdk = new TutorialSDK(); console.log(chalk.cyan('\n๐Ÿ“š Create a New Tutorial\n')); let config; let steps = []; if (options.template) { // Use template const template = TutorialSDK.createTemplate(options.template); config = template.config; steps = template.steps; console.log(chalk.yellow(`Using template: ${options.template}\n`)); // Allow customization const customize = await confirm({ message: 'Would you like to customize the template?', default: true }); if (customize) { config = await customizeTutorialConfig(config); steps = await customizeTutorialSteps(steps); } } else { // Create from scratch config = await createTutorialConfig(); steps = await createTutorialSteps(); } // Generate unique ID if not set if (config.id === 'feature-onboarding-template' || !config.id) { config.id = `tutorial-${Date.now()}`; } const spinner = ora('Creating tutorial...').start(); try { await sdk.createTutorial(config, steps); spinner.succeed('Tutorial created successfully!'); console.log(chalk.cyan('\n๐Ÿ“‹ Tutorial Summary:')); console.log(` ID: ${chalk.white(config.id)}`); console.log(` Title: ${chalk.white(config.title)}`); console.log(` Steps: ${chalk.white(steps.length)}`); console.log(` Target: ${chalk.white(config.targetAudience)}`); console.log(` Time: ${chalk.white(config.estimatedTime)}`); console.log(chalk.gray('\n๐Ÿ’ก Next steps:')); console.log(chalk.white(` 1. Test locally: pame-core tutorial test ${config.id}`)); console.log(chalk.white(` 2. Deploy to project: pame-core tutorial deploy ${config.id} <project-path>`)); } catch (error) { spinner.fail('Failed to create tutorial'); console.error(chalk.red('Error:'), error.message); } } async function createTutorialConfig() { const title = await input({ message: 'Tutorial title:', validate: (value) => value.length > 0 || 'Title is required' }); const description = await input({ message: 'Tutorial description:', validate: (value) => value.length > 0 || 'Description is required' }); const targetAudience = await select({ message: 'Target audience:', choices: [ { value: 'developer', name: '๐Ÿ‘จโ€๐Ÿ’ป Developer' }, { value: 'end-user', name: '๐Ÿ‘ค End User' }, { value: 'team-member', name: '๐Ÿ‘ฅ Team Member' }, { value: 'enterprise', name: '๐Ÿข Enterprise' } ] }); const estimatedTime = await input({ message: 'Estimated completion time:', default: '15 minutes' }); const outcomes = await input({ message: 'Learning outcomes (comma-separated):', validate: (value) => value.length > 0 || 'At least one outcome is required' }); const prerequisites = await input({ message: 'Prerequisites (comma-separated, optional):', default: '' }); const enableAnalytics = await confirm({ message: 'Enable analytics tracking?', default: true }); return { id: `tutorial-${Date.now()}`, version: '1.0.0', title, description, targetAudience: targetAudience, estimatedTime, outcomes: outcomes.split(',').map((o) => o.trim()).filter((o) => o), prerequisites: prerequisites ? prerequisites.split(',').map((p) => p.trim()).filter((p) => p) : undefined, analytics: enableAnalytics ? { trackingEnabled: true } : undefined }; } async function customizeTutorialConfig(template) { console.log(chalk.cyan('๐Ÿ“ Customize Tutorial Configuration\n')); const title = await input({ message: 'Tutorial title:', default: template.title }); const description = await input({ message: 'Tutorial description:', default: template.description }); const estimatedTime = await input({ message: 'Estimated completion time:', default: template.estimatedTime }); return { ...template, title, description, estimatedTime }; } async function createTutorialSteps() { const steps = []; let addMore = true; console.log(chalk.cyan('\n๐Ÿ“ Create Tutorial Steps\n')); while (addMore) { const step = await createSingleStep(steps.length + 1); steps.push(step); addMore = await confirm({ message: 'Add another step?', default: true }); } return steps; } async function createSingleStep(stepNumber) { console.log(chalk.yellow(`\n๐Ÿ“ Step ${stepNumber}:`)); const title = await input({ message: 'Step title:', validate: (value) => value.length > 0 || 'Title is required' }); const description = await input({ message: 'Step description:', validate: (value) => value.length > 0 || 'Description is required' }); const type = await select({ message: 'Step type:', choices: [ { value: 'instruction', name: '๐Ÿ“– Instruction - Information or explanation' }, { value: 'action', name: 'โšก Action - User performs an action' }, { value: 'validation', name: 'โœ… Validation - Check completion' }, { value: 'decision', name: '๐Ÿ”€ Decision - Branching logic' }, { value: 'milestone', name: '๐Ÿ† Milestone - Achievement point' } ] }); const content = {}; // Based on type, collect appropriate content if (type === 'instruction') { content.instruction = await input({ message: 'Instruction text:' }); } else if (type === 'action') { const actionType = await select({ message: 'Action type:', choices: [ { value: 'command', name: 'Run a command' }, { value: 'code', name: 'Show code example' }, { value: 'input', name: 'Collect user input' } ] }); if (actionType === 'command') { content.command = { command: await input({ message: 'Command to run:' }), description: await input({ message: 'Command description:' }) }; } else if (actionType === 'code') { content.codeExample = { language: await input({ message: 'Programming language:', default: 'typescript' }), code: await input({ message: 'Code snippet:' }) }; } else if (actionType === 'input') { content.userInput = { prompt: await input({ message: 'Input prompt:' }), type: 'text' }; } } const addHints = await confirm({ message: 'Add hints for this step?', default: false }); let hints = []; if (addHints) { const hintsInput = await input({ message: 'Enter hints (comma-separated):' }); hints = hintsInput.split(',').map(h => h.trim()).filter(h => h); } return { id: `step-${stepNumber}`, title, description, type: type, content, hints: hints.length > 0 ? hints : undefined }; } async function customizeTutorialSteps(templateSteps) { console.log(chalk.cyan('\n๐Ÿ“ Customize Tutorial Steps\n')); const action = await select({ message: 'How would you like to customize the steps?', choices: [ { value: 'keep', name: 'Keep template steps as-is' }, { value: 'modify', name: 'Modify existing steps' }, { value: 'add', name: 'Add additional steps' }, { value: 'rebuild', name: 'Rebuild from scratch' } ] }); if (action === 'keep') { return templateSteps; } else if (action === 'rebuild') { return await createTutorialSteps(); } else if (action === 'add') { const additionalSteps = await createTutorialSteps(); return [...templateSteps, ...additionalSteps]; } else { // Modify existing - simplified for now console.log(chalk.yellow('Step modification not yet implemented. Using template steps.')); return templateSteps; } } async function deployTutorial(tutorialId, projectPath, options) { const sdk = new TutorialSDK(); console.log(chalk.cyan('\n๐Ÿš€ Deploy Tutorial\n')); const resolvedPath = path.resolve(projectPath); const spinner = ora(`Deploying tutorial to ${resolvedPath}...`).start(); try { await sdk.deployTutorial(tutorialId, resolvedPath, { autoStart: options.autoStart, targetFile: options.targetFile }); spinner.succeed('Tutorial deployed successfully!'); if (!options.autoStart) { console.log(chalk.gray('\n๐Ÿ’ก Tutorial has been deployed to the project.')); console.log(chalk.gray('Users will see the tutorial when they run the start script.')); } } catch (error) { spinner.fail('Failed to deploy tutorial'); console.error(chalk.red('Error:'), error.message); } } async function listTutorials() { const sdk = new TutorialSDK(); console.log(chalk.cyan('\n๐Ÿ“š Available Tutorials\n')); // In a real implementation, this would scan the tutorials directory console.log(chalk.gray('Feature coming soon...')); console.log(chalk.gray('This will list all created tutorials with their metadata.')); } async function showAnalytics(tutorialId) { const sdk = new TutorialSDK(); console.log(chalk.cyan('\n๐Ÿ“Š Tutorial Analytics\n')); const spinner = ora('Loading analytics...').start(); try { const metrics = await sdk.generateAnalytics(tutorialId); spinner.stop(); console.log(chalk.white(`Tutorial: ${tutorialId}\n`)); console.log(chalk.cyan('๐Ÿ“ˆ Overview:')); console.log(` Total Starts: ${chalk.white(metrics.totalStarts)}`); console.log(` Completions: ${chalk.white(metrics.totalCompletions)}`); console.log(` Completion Rate: ${chalk.white(Math.round((metrics.totalCompletions / metrics.totalStarts) * 100) || 0)}%`); console.log(` Avg Time: ${chalk.white(Math.round(metrics.averageCompletionTime / 60) || 0)} minutes`); console.log(` Avg Rating: ${chalk.white(metrics.averageRating || 'N/A')} / 5`); if (Object.keys(metrics.dropOffPoints).length > 0) { console.log(chalk.cyan('\n๐Ÿ“ Drop-off Points:')); Object.entries(metrics.dropOffPoints) .sort(([, a], [, b]) => b - a) .slice(0, 5) .forEach(([stepId, count]) => { console.log(` ${stepId}: ${chalk.red(count + ' users')}`); }); } if (metrics.commonIssues.length > 0) { console.log(chalk.cyan('\nโš ๏ธ Common Issues:')); metrics.commonIssues.slice(0, 5).forEach((issue, i) => { console.log(` ${i + 1}. ${issue}`); }); } } catch (error) { spinner.fail('Failed to load analytics'); console.error(chalk.red('Error:'), error.message); } } async function testTutorial(tutorialId) { console.log(chalk.cyan('\n๐Ÿงช Test Tutorial\n')); console.log(chalk.gray('Feature coming soon...')); console.log(chalk.gray('This will allow you to run through the tutorial locally to test it.')); } //# sourceMappingURL=tutorial.js.map