pame-core-cli
Version:
PAME.AI Core Operating System CLI - Open Source AI Platform for Agentic Commerce
362 lines โข 14.2 kB
JavaScript
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