hackages
Version:
CLI tool for learning software development concepts through test-driven development
149 lines (148 loc) • 7.73 kB
JavaScript
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."));
}
}