claude-data-academy
Version:
Claude Code용 데이터 학습 선생님 에이전트 NPX 설치 도구
313 lines (261 loc) • 9.87 kB
JavaScript
const fs = require('fs-extra');
const path = require('path');
const chalk = require('chalk');
const { validateInstallation } = require('./validator');
const {
isValidTeacher,
isValidLevel,
getTeacherInfo,
getLevelInfo,
getCurrentDate,
writeJsonFile,
readJsonFile,
copyFileWithLogging,
ensureDirectoryWithLogging
} = require('./utils');
/**
* 선생님 에이전트들 설치
* @param {Array} teacherNames - 설치할 선생님 목록
* @param {string} level - 학습 수준 (beginner, intermediate, advanced)
* @param {string} agentsPath - agents 디렉토리 경로
* @param {Object} options - 추가 옵션
*/
async function installTeachers(teacherNames, level, agentsPath, options = {}) {
const { verbose = false, force = false } = options;
console.log(chalk.blue('\n📦 선생님 설치를 시작합니다...\n'));
// 입력 검증
const invalidTeachers = teacherNames.filter(name => !isValidTeacher(name));
if (invalidTeachers.length > 0) {
throw new Error(`유효하지 않은 선생님: ${invalidTeachers.join(', ')}`);
}
if (!isValidLevel(level)) {
throw new Error(`유효하지 않은 학습 수준: ${level}`);
}
// 설치 전 검증
const validation = await validateInstallation(agentsPath, teacherNames);
if (!validation.canInstall) {
throw new Error(validation.warnings.join(', '));
}
// 충돌 처리
if (validation.conflicts.length > 0 && !force) {
console.log(chalk.yellow('⚠️ 다음 선생님들이 이미 설치되어 있습니다:'));
validation.conflicts.forEach(conflict => {
const teacherInfo = getTeacherInfo(conflict.teacher);
console.log(chalk.yellow(` ${teacherInfo.emoji} ${teacherInfo.name}`));
});
console.log(chalk.gray(' 덮어쓰려면 --force 옵션을 사용하세요.\n'));
}
// 실제 설치 진행
const installResults = [];
const levelInfo = getLevelInfo(level);
for (const teacherName of teacherNames) {
const teacherInfo = getTeacherInfo(teacherName);
try {
console.log(chalk.cyan(`📚 ${teacherInfo.emoji} ${teacherInfo.name} 설치 중...`));
// 선생님 파일 설치
await installSingleTeacher(teacherName, level, agentsPath, verbose);
installResults.push({
teacher: teacherName,
success: true,
info: teacherInfo
});
console.log(chalk.green(`✅ ${teacherInfo.name} 설치 완료`));
} catch (error) {
console.error(chalk.red(`❌ ${teacherInfo.name} 설치 실패: ${error.message}`));
installResults.push({
teacher: teacherName,
success: false,
error: error.message,
info: teacherInfo
});
}
}
// 학습 가이드 생성
console.log(chalk.cyan('\n📋 학습 가이드 생성 중...'));
await generateLearningGuide(agentsPath, installResults, level);
// 진도 추적 파일 생성/업데이트
console.log(chalk.cyan('📊 진도 추적 시스템 설정 중...'));
await initializeProgressTracking(agentsPath, installResults, level);
// 설치 완료 메시지
const successCount = installResults.filter(r => r.success).length;
const failCount = installResults.length - successCount;
console.log(chalk.green(`\n🎉 설치 완료!`));
console.log(chalk.green(`✅ 성공: ${successCount}개`));
if (failCount > 0) {
console.log(chalk.red(`❌ 실패: ${failCount}개`));
}
console.log(chalk.blue(`\n🎯 학습 시작!`));
console.log(chalk.gray(' 예: "@data-engineering-teacher 안녕하세요, 첫 수업을 시작하고 싶어요"'));
console.log(chalk.gray(`\n📚 학습 가이드: ${path.join(agentsPath, 'LEARNING_GUIDE.md')}`));
console.log(chalk.gray(`📊 진도 체크: ${path.join(agentsPath, 'progress.json')}`));
return installResults;
}
/**
* 단일 선생님 설치
* @param {string} teacherName - 선생님 이름
* @param {string} level - 학습 수준
* @param {string} agentsPath - agents 디렉토리 경로
* @param {boolean} verbose - 상세 로그
*/
async function installSingleTeacher(teacherName, level, agentsPath, verbose = false) {
const teacherInfo = getTeacherInfo(teacherName);
const teachersDir = path.join(__dirname, '..', 'teachers');
const sourceFile = path.join(teachersDir, teacherInfo.fileName);
const destFile = path.join(agentsPath, teacherInfo.fileName);
// 템플릿 파일 읽기
if (!await fs.pathExists(sourceFile)) {
throw new Error(`선생님 템플릿 파일을 찾을 수 없습니다: ${sourceFile}`);
}
// 파일 복사
await copyFileWithLogging(sourceFile, destFile, verbose);
if (verbose) {
console.log(chalk.gray(` 📝 ${teacherInfo.name} 파일 생성 완료`));
}
}
/**
* 학습 가이드 생성
* @param {string} agentsPath - agents 디렉토리 경로
* @param {Array} installResults - 설치 결과
* @param {string} level - 학습 수준
*/
async function generateLearningGuide(agentsPath, installResults, level) {
const guidePath = path.join(agentsPath, 'LEARNING_GUIDE.md');
const levelInfo = getLevelInfo(level);
const successfulInstalls = installResults.filter(r => r.success);
let content = `# 📚 데이터 학습 가이드
설치일: ${getCurrentDate()}
학습 수준: ${levelInfo.emoji} ${levelInfo.name} - ${levelInfo.description}
## 설치된 선생님들
`;
successfulInstalls.forEach(result => {
const info = result.info;
content += `- ${info.emoji} **${info.name}** - ${info.description}\n`;
});
content += `
## 학습 원칙
### 🚫 선생님들이 하지 않는 것
- 완성된 코드를 바로 제공하지 않음
- 답을 직접적으로 알려주지 않음
- 대신 해서 주지 않음
### ✅ 선생님들이 하는 것
- 단계적 학습 가이드 제공
- 적절한 힌트와 질문으로 사고 유도
- 스스로 구현할 수 있도록 도움
- 코드 리뷰와 개선 방향 제시
- 학습 진도 체크와 피드백
## 효과적인 학습 방법
1. **주도적 학습**: 선생님이 주는 과제를 스스로 해결해보기
2. **질문하기**: 막힐 때 "이 부분이 왜 안 될까요?" 보다는 "이렇게 해봤는데 어떻게 생각하세요?"
3. **코드 공유**: 작성한 코드를 공유하고 리뷰 받기
4. **진도 체크**: 각 단계별 체크포인트 확인하기
## 사용 예시
\`\`\`
학습자: "@data-engineering-teacher 안녕하세요, Kafka 학습을 시작하고 싶어요"
선생님: "안녕하세요! Kafka 학습을 시작하기 전에 몇 가지 질문드릴게요.
1. 메시지 큐를 사용해본 경험이 있나요?
2. 실시간 처리가 왜 필요한지 생각해본 적 있나요?
3. 어떤 상황에서 Kafka를 써보고 싶으신가요?
이 질문들에 답하시면서 Kafka의 필요성부터 차근차근 이해해봐요!"
\`\`\`
`;
await fs.writeFile(guidePath, content, 'utf-8');
}
/**
* 진도 추적 시스템 초기화
* @param {string} agentsPath - agents 디렉토리 경로
* @param {Array} installResults - 설치 결과
* @param {string} level - 학습 수준
*/
async function initializeProgressTracking(agentsPath, installResults, level) {
const progressPath = path.join(agentsPath, 'progress.json');
const currentDate = getCurrentDate();
// 기존 진도 파일 읽기
let progressData = await readJsonFile(progressPath) || {
learner: {
level: level,
startDate: currentDate,
currentWeek: 1
},
teachers: {},
achievements: []
};
// 새로 설치된 선생님들 추가
const successfulInstalls = installResults.filter(r => r.success);
successfulInstalls.forEach(result => {
const teacherKey = result.teacher.replace('-', '_');
if (!progressData.teachers[teacherKey]) {
progressData.teachers[teacherKey] = {
enrolled: true,
enrollDate: currentDate,
currentTopic: getInitialTopic(result.teacher, level),
progress: 0,
completedTasks: 0,
totalTasks: getTotalTasksForLevel(level)
};
}
});
// 설치 달성 기록
if (successfulInstalls.length > 0) {
progressData.achievements.push({
title: `${successfulInstalls.length}명의 선생님과 학습 시작`,
date: currentDate,
teachers: successfulInstalls.map(r => r.teacher)
});
}
await writeJsonFile(progressPath, progressData);
}
/**
* 학습 수준별 초기 주제 반환
* @param {string} teacherName - 선생님 이름
* @param {string} level - 학습 수준
* @returns {string} 초기 주제
*/
function getInitialTopic(teacherName, level) {
const topics = {
'data-engineer': {
'beginner': 'etl-basics',
'intermediate': 'pipeline-design',
'advanced': 'distributed-systems'
},
'data-architect': {
'beginner': 'data-modeling-basics',
'intermediate': 'architecture-patterns',
'advanced': 'scalable-architecture'
},
'data-analyst': {
'beginner': 'sql-fundamentals',
'intermediate': 'advanced-analytics',
'advanced': 'statistical-modeling'
},
'data-scientist': {
'beginner': 'ml-fundamentals',
'intermediate': 'model-development',
'advanced': 'advanced-ml'
},
'ai-engineer': {
'beginner': 'mlops-basics',
'intermediate': 'deployment-strategies',
'advanced': 'production-optimization'
}
};
return topics[teacherName]?.[level] || 'introduction';
}
/**
* 학습 수준별 총 과제 수 반환
* @param {string} level - 학습 수준
* @returns {number} 총 과제 수
*/
function getTotalTasksForLevel(level) {
const taskCounts = {
'beginner': 8,
'intermediate': 12,
'advanced': 16
};
return taskCounts[level] || 10;
}
module.exports = {
installTeachers,
installSingleTeacher,
generateLearningGuide,
initializeProgressTracking
};