@morodomi/ait3
Version:
AIT³ Development Platform - AI + Ticket + Test + Tool driven development methodology
437 lines (417 loc) • 17 kB
JavaScript
import { writeFile, access, readFile } from 'fs/promises';
import { STYLES } from '../../common/styles.js';
import { ensureMultipleDirectories } from '../../common/file-operations.js';
import { generateComprehensiveTemplate, generateDockerSection, generateAiGuidelinesSection } from '../../common/claude-md-templates.js';
export async function initClaudeMdCommand(args) {
try {
// Analyze project
const analysis = await analyzeProject();
// Create output directories
const createdDirectories = await ensureDirectories();
// Generate files
await generateAnalysisFiles(analysis);
await generateClaudeMdTemplate(analysis);
// Build result message
const messages = [];
// Add directory creation messages
for (const dir of createdDirectories) {
messages.push(`${STYLES.success('SUCCESS:')} Created directory: ${STYLES.info(dir)}`);
}
if (analysis.language !== 'unknown') {
if (analysis.language === 'Node.js TypeScript') {
messages.push(`${STYLES.success('SUCCESS:')} Node.js TypeScript project detected`);
}
else if (analysis.language === 'Python') {
messages.push(`${STYLES.success('SUCCESS:')} Python project detected`);
}
else if (analysis.language === 'Go') {
messages.push(`${STYLES.success('SUCCESS:')} Go project detected`);
}
else if (analysis.language === 'PHP') {
messages.push(`${STYLES.success('SUCCESS:')} PHP Composer project detected`);
}
else {
messages.push(`${STYLES.success('SUCCESS:')} ${analysis.language} project detected`);
}
}
else {
messages.push(`${STYLES.warning('⚠')} Unknown project type`);
messages.push(`${STYLES.info('INFO:')} Generic template generated`);
}
if (analysis.docker.hasDockerfile) {
messages.push(`${STYLES.success('SUCCESS:')} Docker detected: Dockerfile`);
}
if (analysis.docker.hasCompose) {
messages.push(`${STYLES.success('SUCCESS:')} Docker Compose detected: ${analysis.docker.composeFile}`);
}
if (analysis.testFramework !== 'unknown') {
messages.push(`${STYLES.success('SUCCESS:')} ${analysis.testFramework} test framework detected`);
}
if (analysis.architecture === 'Pure Functions + Service Injection') {
messages.push(`${STYLES.success('SUCCESS:')} AIT³ architecture detected: ${analysis.architecture}`);
}
// Add commands
messages.push(`${STYLES.info('INFO:')} ${analysis.commands.install}`);
messages.push(`${STYLES.info('INFO:')} ${analysis.commands.test}`);
messages.push(`${STYLES.info('INFO:')} ${analysis.commands.build}`);
// Add CLAUDE.md status
try {
await access('CLAUDE.md.existing');
messages.push('');
messages.push(`${STYLES.warning('⚠')} Existing CLAUDE.md detected`);
messages.push(`${STYLES.success('SUCCESS:')} Created CLAUDE.md.existing (backup)`);
messages.push(`${STYLES.success('SUCCESS:')} Created CLAUDE.md.new (new template)`);
messages.push(`${STYLES.success('SUCCESS:')} Created CLAUDE_MD_MERGE_GUIDE.md`);
messages.push('');
messages.push(`${STYLES.info('Next step:')} Review the files and merge them with Claude Code`);
}
catch {
messages.push('');
messages.push(`${STYLES.success('SUCCESS:')} Created CLAUDE.md`);
messages.push(`${STYLES.success('SUCCESS:')} Created CLAUDE_MD_GUIDE.md`);
messages.push('');
messages.push(`${STYLES.info('Next step:')} Review and customize CLAUDE.md with Claude Code`);
}
return {
success: true,
message: messages.join('\n')
};
}
catch (error) {
if (error instanceof Error && error.message.includes('read-only')) {
return {
success: false,
message: `${STYLES.danger('ERROR: Failed to create')}: Permission denied`
};
}
if (error instanceof Error && error.message.includes('Malformed package.json')) {
// For malformed package.json, still generate files but with warning
try {
await ensureDirectories();
const defaultAnalysis = {
language: 'unknown',
framework: 'unknown',
buildSystem: 'unknown',
testFramework: 'unknown',
commands: {
install: 'echo "No install command detected"',
test: 'echo "No test command detected"',
build: 'echo "No build command detected"',
dev: 'echo "No dev command detected"'
},
docker: {
hasDockerfile: false,
hasCompose: false
},
architecture: 'Unknown',
projectName: 'test-project'
};
await generateAnalysisFiles(defaultAnalysis);
await generateClaudeMdTemplate(defaultAnalysis);
return {
success: true,
message: `${STYLES.warning('⚠')} Malformed package.json
${STYLES.info('INFO:')} Generic template generated`
};
}
catch {
return {
success: false,
message: `${STYLES.danger('ERROR: Failed to create')}: ${error instanceof Error ? error.message : 'Unknown error'}`
};
}
}
return {
success: false,
message: `${STYLES.danger('ERROR: Failed to create')}: ${error instanceof Error ? error.message : 'Unknown error'}`
};
}
}
async function analyzeProject() {
const analysis = {
language: 'unknown',
framework: 'unknown',
buildSystem: 'unknown',
testFramework: 'unknown',
commands: {
install: 'echo "No install command detected"',
test: 'echo "No test command detected"',
build: 'echo "No build command detected"',
dev: 'echo "No dev command detected"'
},
docker: {
hasDockerfile: false,
hasCompose: false
},
architecture: 'Unknown',
projectName: 'test-project'
};
// Check for Node.js/TypeScript
try {
await access('package.json');
const packageContent = await readFile('package.json', 'utf-8');
try {
const pkg = JSON.parse(packageContent);
analysis.projectName = pkg.name || 'test-project';
analysis.language = 'Node.js';
analysis.buildSystem = 'npm';
// Check for TypeScript
if (pkg.devDependencies?.typescript || pkg.dependencies?.typescript) {
analysis.language = 'Node.js TypeScript';
try {
await access('tsconfig.json');
analysis.framework = 'TypeScript';
}
catch {
// No tsconfig but has typescript dependency
}
}
// Detect test framework
if (pkg.devDependencies?.vitest || pkg.dependencies?.vitest) {
analysis.testFramework = 'Vitest';
}
else if (pkg.devDependencies?.jest || pkg.dependencies?.jest) {
analysis.testFramework = 'Jest';
}
// Extract commands from scripts
if (pkg.scripts) {
analysis.commands.install = 'npm install';
analysis.commands.test = pkg.scripts.test ? 'npm test' : 'npm run test';
analysis.commands.build = pkg.scripts.build ? 'npm run build' : 'echo "No build script"';
analysis.commands.dev = pkg.scripts.dev ? 'npm run dev' : pkg.scripts.start ? 'npm start' : 'echo "No dev script"';
}
}
catch {
throw new Error('Malformed package.json');
}
}
catch (fileError) {
// Check if it's a malformed package.json error (not a file access error)
if (fileError instanceof Error && fileError.message === 'Malformed package.json') {
throw fileError;
}
// Check for PHP
try {
await access('composer.json');
analysis.language = 'PHP';
analysis.framework = 'Composer';
analysis.buildSystem = 'composer';
analysis.commands.install = 'composer install';
analysis.commands.test = 'composer test';
analysis.commands.build = 'composer dump-autoload';
analysis.commands.dev = 'php -S localhost:8000';
}
catch {
// Check for Python
try {
await access('requirements.txt');
analysis.language = 'Python';
analysis.framework = 'pip';
analysis.buildSystem = 'pip';
analysis.commands.install = 'pip install -r requirements.txt';
analysis.commands.test = 'pytest';
analysis.commands.build = 'python -m build';
analysis.commands.dev = 'python app.py';
}
catch {
// Check for Python with pyproject.toml
try {
await access('pyproject.toml');
analysis.language = 'Python';
analysis.framework = 'poetry';
analysis.buildSystem = 'poetry';
analysis.commands.install = 'poetry install';
analysis.commands.test = 'pytest';
analysis.commands.build = 'poetry build';
analysis.commands.dev = 'poetry run python app.py';
}
catch {
// Check for Go
try {
await access('go.mod');
analysis.language = 'Go';
analysis.framework = 'Go modules';
analysis.buildSystem = 'go';
analysis.commands.install = 'go mod tidy';
analysis.commands.test = 'go test ./...';
analysis.commands.build = 'go build';
analysis.commands.dev = 'go run main.go';
}
catch {
// Unknown project type
}
}
}
}
}
// Check for Docker
try {
await access('Dockerfile');
analysis.docker.hasDockerfile = true;
}
catch {
// No Dockerfile
}
// Check for Docker Compose (new format first)
try {
await access('compose.yml');
analysis.docker.hasCompose = true;
analysis.docker.composeFile = 'compose.yml';
}
catch {
try {
await access('compose.yaml');
analysis.docker.hasCompose = true;
analysis.docker.composeFile = 'compose.yaml';
}
catch {
try {
await access('docker-compose.yml');
analysis.docker.hasCompose = true;
analysis.docker.composeFile = 'docker-compose.yml';
}
catch {
// No compose file
}
}
}
// Check for AIT³ architecture pattern
try {
await access('src/commands');
await access('src/services');
await access('src/common');
analysis.architecture = 'Pure Functions + Service Injection';
}
catch {
// Not AIT³ architecture
}
return analysis;
}
async function ensureDirectories() {
const result = await ensureMultipleDirectories([
'src/assets/templates',
'docs/references'
]);
if (!result.success) {
throw new Error(result.message || 'Failed to create directories');
}
return result.createdDirectories;
}
async function generateAnalysisFiles(analysis) {
// Generate project analysis file
const analysisContent = `# Project Analysis Results
Generated: ${new Date().toISOString()}
Project: ${analysis.projectName}
## Detected Language
${analysis.language}
## Build System
${analysis.buildSystem}
## Commands
- Install: ${analysis.commands.install}
- Test: ${analysis.commands.test}
- Build: ${analysis.commands.build}
- Dev: ${analysis.commands.dev}
## Docker Configuration
- Dockerfile: ${analysis.docker.hasDockerfile ? 'Yes' : 'No'}
- Docker Compose: ${analysis.docker.hasCompose ? `Yes (${analysis.docker.composeFile})` : 'No'}
## Architecture
${analysis.architecture}
## Test Framework
${analysis.testFramework}
`;
await writeFile('docs/references/project-analysis.md', analysisContent, 'utf-8');
// Generate detected commands file
const commandsContent = `# Detected Commands
## Install Dependencies
\`\`\`bash
${analysis.commands.install}
\`\`\`
## Run Tests
\`\`\`bash
${analysis.commands.test}
\`\`\`
## Build Project
\`\`\`bash
${analysis.commands.build}
\`\`\`
## Development Server
\`\`\`bash
${analysis.commands.dev}
\`\`\`
`;
await writeFile('docs/references/detected-commands.md', commandsContent, 'utf-8');
}
async function generateClaudeMdTemplate(analysis) {
const templateVariables = {
projectName: analysis.projectName,
language: analysis.language,
framework: analysis.framework,
architecture: analysis.architecture,
testFramework: analysis.testFramework,
buildSystem: analysis.buildSystem,
commands: analysis.commands
};
// Generate base template content
let templateContent = generateComprehensiveTemplate(templateVariables);
// Add Docker section if Docker is detected
if (analysis.docker.hasDockerfile) {
templateContent += generateDockerSection(analysis.docker.hasDockerfile, analysis.docker.composeFile);
}
// Add AI guidelines section
templateContent += '\n\n' + generateAiGuidelinesSection();
// Check if CLAUDE.md already exists
let existingClaudeMd = null;
try {
existingClaudeMd = await readFile('CLAUDE.md', 'utf-8');
}
catch {
// CLAUDE.md doesn't exist
}
if (existingClaudeMd) {
// Save as separate files for comparison
await writeFile('CLAUDE.md.new', templateContent, 'utf-8');
await writeFile('CLAUDE.md.existing', existingClaudeMd, 'utf-8');
// Add merge instructions
const mergeInstructions = `# CLAUDE.md Merge Instructions
You have both an existing CLAUDE.md and a newly generated template.
## Files Created:
- **CLAUDE.md.existing**: Your current CLAUDE.md file
- **CLAUDE.md.new**: Newly generated template based on project analysis
- **docs/references/project-analysis.md**: Detailed project analysis
## Next Steps:
1. Review all three files
2. Merge the best parts of both CLAUDE.md files
3. Include project-specific knowledge from the existing file
4. Add newly detected features from the analysis
5. Save the final version as CLAUDE.md
## Claude Code Command:
After reviewing the files, you can ask Claude to merge them:
"Please merge CLAUDE.md.existing and CLAUDE.md.new, keeping the best of both and incorporating the project analysis results."
`;
await writeFile('CLAUDE_MD_MERGE_GUIDE.md', mergeInstructions, 'utf-8');
}
else {
// No existing CLAUDE.md, create it directly
await writeFile('CLAUDE.md', templateContent, 'utf-8');
// Add customization guide
const customizationGuide = `# CLAUDE.md Customization Guide
A new CLAUDE.md has been generated for your project.
## Files Created:
- **CLAUDE.md**: Initial template based on project analysis
- **docs/references/project-analysis.md**: Detailed project analysis
- **docs/references/detected-commands.md**: Detected project commands
## Next Steps:
1. Review the generated CLAUDE.md
2. Add project-specific business logic details
3. Include any special setup instructions
4. Document non-obvious behaviors or gotchas
5. Add team conventions and standards
## Claude Code Command:
You can ask Claude to enhance the CLAUDE.md:
"Please analyze this project more deeply and enhance the CLAUDE.md with specific business logic, patterns, and important details I should know when working on this codebase."
`;
await writeFile('CLAUDE_MD_GUIDE.md', customizationGuide, 'utf-8');
}
// Always save template for reference
await writeFile('src/assets/templates/claude-md-template.md', templateContent, 'utf-8');
}