hackages
Version:
CLI tool for learning software development concepts through test-driven development
122 lines (121 loc) • 6.7 kB
JavaScript
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);
}
}