UNPKG

hackages

Version:

CLI tool for learning software development concepts through test-driven development

149 lines (148 loc) 7.73 kB
import chalk from "chalk"; import prompts from "prompts"; import { clearScreen, printInfo, printError } from "../utils/console.js"; import { generateLearningExercise } from "../services/claude.service.js"; import { createExerciseFiles, cloneRepositoryTemplate, createExerciseFilesInRepository } from "../services/file-manager.js"; import { getTechnologyConfigWithRepository } from "../services/tech.service.js"; import path from "path"; import { getCurrentUser, writeLearningGoal } from "../services/auth.js"; import { WelcomeMessage } from "../utils/welcomeMessage.js"; const ONBOARDING_TOPICS = { javascript: { title: "JavaScript Array Map", description: "Learn how to use the map() function", topic: "JavaScript array map function to transform elements", technology: "JavaScript" }, typescript: { title: "TypeScript Function Types", description: "Learn function parameter and return types", topic: "TypeScript function parameter and return type annotations", technology: "TypeScript" }, surprise: { title: "Surprise Me!", description: "Let Hackages pick something fun for you", topic: "A simple programming concept to get you started", technology: "TypeScript" // Default for surprise } }; export async function onboardingCommand() { clearScreen(); WelcomeMessage(); // Offer 3 simple options console.log(chalk.bold.green("🎯 Ready to try? Pick one:")); console.log(); const response = await prompts({ type: "select", name: "choice", message: "Choose your first exercise:", choices: [ { title: `1. ${ONBOARDING_TOPICS.javascript.title}`, description: ONBOARDING_TOPICS.javascript.description, value: "javascript" }, { title: `2. ${ONBOARDING_TOPICS.typescript.title}`, description: ONBOARDING_TOPICS.typescript.description, value: "typescript" }, { title: `3. ${ONBOARDING_TOPICS.surprise.title}`, description: ONBOARDING_TOPICS.surprise.description, value: "surprise" } ], initial: 0 }); if (!response.choice) { console.log(chalk.gray("👋 Come back when you're ready to learn!")); return; } const selectedTopic = ONBOARDING_TOPICS[response.choice]; console.log(); console.log(chalk.bold.green("✨ Great choice!")); console.log(chalk.white(`You'll be learning: ${selectedTopic.title}`)); console.log(); // Generate the onboarding exercise try { console.log(chalk.yellow("🔄 Generating your first exercise/game...")); const currentUser = getCurrentUser(); const learningGoal = { id: "onboarding-exercise", topic: selectedTopic.topic, motivation: "Discovering journey", timeCommitment: "5 minutes", selectedTech: selectedTopic.technology, skillLevel: "Beginner", onboarding: true, next: undefined, userHandle: currentUser?.login || currentUser?.anonymousLogin, }; // write the learning goal to a file called learning.json in the .hackages directory at HOME directory writeLearningGoal(learningGoal); // Generate exercise with onboarding flag const exerciseContent = await generateLearningExercise(learningGoal); // Get technology configuration with repository templates const techConfig = getTechnologyConfigWithRepository(selectedTopic.technology.toLowerCase()); // Create a safe exercise name from the topic const exerciseName = selectedTopic.title.replace(/[^a-zA-Z0-9\s]/g, '').replace(/\s+/g, '-').toLowerCase(); let exerciseFiles; let repoPath = null; try { // Try to clone repository template if available if (techConfig.repository) { repoPath = cloneRepositoryTemplate(techConfig, exerciseName); exerciseFiles = createExerciseFilesInRepository(selectedTopic.topic, exerciseContent, techConfig, repoPath); } else { // Fallback to creating files in current directory printInfo("📁 No repository template available, creating files in current directory..."); exerciseFiles = createExerciseFiles(selectedTopic.topic, exerciseContent, techConfig); } // Success message console.log(); console.log(chalk.bold.green("🎉 Your first exercise is ready!")); console.log(chalk.cyan("=".repeat(50))); if (repoPath) { const dirName = path.basename(repoPath); const testFileExt = techConfig.extension.replace('.spec', ''); const srcFileExt = techConfig.srcExt; console.log(chalk.green(` ✓ ${dirName}/`), chalk.dim("(Your exercise directory)")); console.log(chalk.green(` ✓ ${dirName}/tests/exercise-1.spec${testFileExt}`), chalk.dim("(Your test cases)")); console.log(chalk.green(` ✓ ${dirName}/src/exercise-1${srcFileExt}`), chalk.dim("(Your implementation file)")); console.log(chalk.green(` ✓ ${dirName}/instructions/exercise-1.mdx`), chalk.dim("(Exercise instructions)")); console.log("\n" + chalk.bold.yellow("🚀 Next steps:")); console.log(chalk.white(`1. cd ${dirName} (navigate to your exercise directory)`)); console.log(chalk.white("2. Read the instructions and test file to understand what to implement")); console.log(chalk.white(`3. Open src/exercise-1${srcFileExt} and start coding`)); console.log(chalk.white("4. Run 'npx hackages test' to validate your implementation")); console.log(chalk.white("5. Run 'npx hackages review' to get AI feedback on your code")); } else { console.log(chalk.bold("📁 Files created:")); console.log(chalk.green(` ✓ tests/${exerciseFiles.testFileName}`), chalk.dim("(Your test cases)")); console.log(chalk.green(` ✓ src/${exerciseFiles.srcFileName}`), chalk.dim("(Your implementation file)")); console.log("\n" + chalk.bold.yellow("🚀 Next steps:")); console.log(chalk.white("1. Read the instructions and test file to understand what to implement")); console.log(chalk.white(`2. Open src/${exerciseFiles.srcFileName} and start coding`)); console.log(chalk.white("3. Run 'npx hackages test' to validate your implementation")); console.log(chalk.white("4. Run 'npx hackages review' to get AI feedback on your code")); } console.log(); console.log(chalk.bold.cyan("💡 Want to continue learning?")); console.log(chalk.white("Run 'npx hackages' again to create more exercises and track your progress!")); } catch (error) { printError(`❌ Failed to create exercise: ${error}`); // Fallback to creating files in current directory printInfo("🔄 Falling back to creating files in current directory..."); exerciseFiles = createExerciseFiles(selectedTopic.topic, exerciseContent, techConfig); } } catch (error) { printError(`❌ Failed to generate exercise: ${error}`); console.log(chalk.gray("Please try again or contact support if the issue persists.")); } }