UNPKG

hackages

Version:

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

122 lines (121 loc) 6.7 kB
import chalk from "chalk"; import path from "path"; import { createExerciseFiles, cloneRepositoryTemplate, createExerciseFilesInRepository } from "../services/file-manager.js"; import { detectTechnology, } from "../utils/tech-detector.js"; import { getTechnologyConfigWithRepository } from "../services/tech.service.js"; import { promptForLearningGoal, promptForTechnology, promptForSkillLevel, promptForMotivation, promptForTimeCommitment, } from "../utils/prompts.js"; import { clearScreen, printError, printInfo } from "../utils/console.js"; import { generateLearningExercise } from "../services/claude.service.js"; import { v4 as uuidv4 } from "uuid"; import { WelcomeMessage } from "../utils/welcomeMessage.js"; import { getCurrentUser, writeLearningGoal } from "../services/auth.js"; export async function generateLearningGoal() { clearScreen(); WelcomeMessage(); try { // Ask for learning goal first const goal = await promptForLearningGoal(); if (!goal) { printError("❌ Please enter a learning goal!"); return; } // Check if language is mentioned in the goal const detection = detectTechnology(goal); const selectedTech = await promptForTechnology(detection.detected, detection.detectedTech); // Ask for skill level const skillLevel = await promptForSkillLevel(); // Ask for motivation const motivation = await promptForMotivation(); // Ask for time commitment const timeCommitment = await promptForTimeCommitment(); // Create learning goal object const uuid = uuidv4(); const currentUser = getCurrentUser(); const newLearningGoal = { id: uuid, topic: goal, motivation, timeCommitment, selectedTech, skillLevel, next: undefined, userHandle: currentUser?.login || currentUser?.anonymousLogin, }; // write to .hackages/learning.json writeLearningGoal(newLearningGoal); // Display summary console.log("\n" + chalk.cyan("=".repeat(50))); console.log(chalk.bold.green("✅ Your Learning Plan for Today:")); console.log(chalk.cyan("=".repeat(50))); console.log(chalk.bold("🎯 Concept:"), chalk.white(goal)); console.log(chalk.bold("🛠️ Language:"), chalk.white(selectedTech)); console.log(chalk.bold("📊 Skill Level:"), chalk.white(skillLevel)); if (motivation) { console.log(chalk.bold("🔥 Motivation:"), chalk.white(motivation)); } if (timeCommitment) { console.log(chalk.bold("⏰ Time:"), chalk.white(timeCommitment)); } return newLearningGoal; } catch (error) { printError(`❌ An error occurred: ${error}`); } } export async function generateContent(learningGoal) { console.log("\n" + chalk.yellow("🔄 Generating your personalized exercise...")); // Get technology configuration with repository templates const techConfig = getTechnologyConfigWithRepository(learningGoal.selectedTech.toLowerCase()); // Generate exercise with Claude const exerciseContent = await generateLearningExercise(learningGoal); // Create a safe exercise name from the goal const exerciseName = learningGoal.topic.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(learningGoal.topic, exerciseContent, techConfig, repoPath); } else { // Fallback to creating files in current directory printInfo("📁 No repository template available, creating files in current directory..."); exerciseFiles = createExerciseFiles(learningGoal.topic, exerciseContent, techConfig); } // Success message console.log("\n" + chalk.bold.green("🎉 Your learning exercise is ready!")); console.log(chalk.cyan("=".repeat(50))); if (repoPath) { const dirName = path.basename(repoPath); console.log(chalk.bold("📁 Repository cloned:")); console.log(chalk.green(` ✓ ${dirName}/`), chalk.dim("(Your exercise directory)")); console.log(chalk.green(` ✓ ${dirName}/tests/exercise-1.spec.ts`), chalk.dim("(Your test cases)")); console.log(chalk.green(` ✓ ${dirName}/src/exercise-1.ts`), 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.ts 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("\n" + chalk.bold.cyan("💡 Pro tip:"), chalk.gray("Start by reading the test file - it tells you exactly what to build!")); } 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(learningGoal.topic, exerciseContent, techConfig); } }