wonder-ai-docs
Version:
AI-assisted documentation management for developers, designed to work seamlessly with Cursor
1,386 lines (1,218 loc) ⢠56.7 kB
JavaScript
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const inquirer = require('inquirer');
const { execSync } = require('child_process');
const packageJson = require('./package.json');
const Anthropic = require('@anthropic-ai/sdk');
// Define the Wonder directory structure
const WONDER_DIRS = [
'.cursor/checkpoints',
'templates/architecture',
'templates/constraints',
'templates/decisions',
'templates/documentation',
'templates/environment',
'templates/issue-tracking',
'templates/project',
'templates/references',
'templates/requirements',
'templates/user-stories'
];
// Configuration management
const CONFIG_FILE = path.join(process.env.HOME || process.env.USERPROFILE, '.wonder', 'config.json');
function loadConfig() {
try {
if (fs.existsSync(CONFIG_FILE)) {
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
// Set default values if not present
return {
llmApiKey: config.llmApiKey,
maxTokens: config.maxTokens || 4000, // Default to 4K tokens (safe for Claude)
...config
};
}
} catch (error) {
console.error('Error loading config:', error.message);
}
return {
maxTokens: 4000 // Default value
};
}
function saveConfig(config) {
try {
fs.mkdirSync(path.dirname(CONFIG_FILE), { recursive: true });
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
return true;
} catch (error) {
console.error('Error saving config:', error.message);
return false;
}
}
async function handleConfig(args) {
const config = loadConfig();
if (args.length === 0 || args[0] === 'status') {
// Show current config
console.log('\nCurrent Wonder configuration:');
console.log('----------------------------');
const llmApiKey = config.llmApiKey || process.env.WONDER_LLM_API_KEY;
const hasApiKey = !!llmApiKey;
console.log('LLM Status:');
console.log(` API Key: ${hasApiKey ? 'Set' : 'Not set'}`);
if (hasApiKey) {
console.log(` Source: ${config.llmApiKey ? 'Config file' : 'Environment variable'}`);
console.log(` Key: ********${llmApiKey.slice(-4)}`);
} else {
console.log(' Note: An Anthropic API key is required for LLM features');
console.log(' Get your key at: https://console.anthropic.com/');
console.log(' Set it using: wonder config set llmApiKey your-anthropic-api-key');
}
console.log('\nAnalysis Settings:');
console.log(` Max Tokens: ${config.maxTokens}`);
console.log(' Note: Higher token limits allow for larger documents and more context');
console.log(' Set using: wonder config set maxTokens <number>');
return;
}
const command = args[0];
let key, value, unsetKey;
switch (command) {
case 'set':
if (args.length !== 3) {
console.log('Usage: wonder config set <key> <value>');
return;
}
key = args[1];
value = args[2];
if (key === 'llmApiKey') {
config.llmApiKey = value;
if (saveConfig(config)) {
console.log('Anthropic API key set successfully');
console.log('Note: You may need to restart your terminal for changes to take effect');
}
} else if (key === 'maxTokens') {
const numValue = parseInt(value, 10);
if (isNaN(numValue) || numValue < 1000 || numValue > 8192) {
console.log('Error: maxTokens must be a number between 1000 and 8192');
return;
}
config.maxTokens = numValue;
if (saveConfig(config)) {
console.log(`Max tokens set to ${numValue}`);
}
} else {
console.log('Unknown configuration key');
}
break;
case 'unset':
if (args.length !== 2) {
console.log('Usage: wonder config unset <key>');
return;
}
unsetKey = args[1];
if (unsetKey in config) {
delete config[unsetKey];
if (saveConfig(config)) {
console.log(`${unsetKey} unset successfully`);
}
} else {
console.log('Configuration key not found');
}
break;
default:
console.log('Unknown config command');
console.log('Available commands:');
console.log(' wonder config - Show current configuration');
console.log(' wonder config set - Set a configuration value');
console.log(' wonder config unset - Remove a configuration value');
}
}
// Main function to handle CLI commands
async function main() {
const args = process.argv.slice(2);
const command = args[0];
if (!command) {
// Show welcome message when no command is provided
const welcomeMessage = fs.readFileSync(path.join(__dirname, 'cli-welcome.md'), 'utf8');
console.log(welcomeMessage);
return;
}
switch (command) {
case 'init':
await initProject(args[1]);
break;
case 'help':
showHelp();
break;
case 'version':
console.log(`wonder-ai-cli version ${packageJson.version}`);
break;
case 'tidy':
await tidyProject(args.slice(1));
break;
case 'config':
await handleConfig(args.slice(1));
break;
case 'list':
await listDirectories();
break;
case 'check':
await checkVersion();
break;
case 'update':
await updateProject();
break;
case 'uninstall':
await uninstallProject();
break;
default:
console.log('Unknown command. Use "wonder help" to see available commands.');
process.exit(1);
}
}
// Initialize a new Wonder project
async function initProject(sourceFile) {
let isDemo;
let originalDir;
try {
isDemo = sourceFile === '--demo';
originalDir = process.cwd();
if (isDemo) {
// Get the path to the demo.md file
const demoPath = path.join(process.cwd(), 'demo.md');
if (!fs.existsSync(demoPath)) {
// If not found in current directory, try the package directory
const packageDemoPath = path.join(__dirname, 'demo.md');
if (!fs.existsSync(packageDemoPath)) {
console.log('Error: Demo file not found. Please ensure the package is properly installed.');
return;
}
sourceFile = packageDemoPath;
} else {
sourceFile = demoPath;
}
console.log('\nš Initializing example project...\n');
}
// Check if .wonder directory already exists
if (fs.existsSync('.wonder')) {
console.log('ā A Wonder Docs project already exists in this directory.');
if (isDemo) {
process.chdir(originalDir);
}
return;
}
let projectName = path.basename(process.cwd());
let projectDescription = '';
let initGit = true;
if (isDemo) {
projectName = 'TaskFlow Demo';
projectDescription = 'A task management application for team collaboration';
}
if (sourceFile) {
// Check if source file exists
if (!fs.existsSync(sourceFile)) {
console.log(`Error: Source file "${sourceFile}" not found.`);
return;
}
// Determine the appropriate directory based on file content
const fileContent = fs.readFileSync(sourceFile, 'utf8');
const fileExt = path.extname(sourceFile).toLowerCase();
if (fileExt !== '.md' && fileExt !== '.txt') {
console.log('Error: Source file must be a .md or .txt file.');
return;
}
// Create .wonder directory structure
fs.mkdirSync('.wonder', { recursive: true });
// Create project config.json
const configPath = path.join('.wonder', 'config.json');
fs.writeFileSync(configPath, JSON.stringify({
projectName: projectName,
projectDescription: projectDescription,
createdAt: new Date().toISOString(),
frameworkVersion: packageJson.version,
cursorIntegration: {
enabled: true,
lastUpdated: new Date().toISOString()
},
updatedAt: new Date().toISOString()
}, null, 2));
console.log('\nCreated project configuration at:', configPath);
// Create all required directories
for (const dir of WONDER_DIRS) {
const fullPath = path.join('.wonder', dir);
fs.mkdirSync(fullPath, { recursive: true });
}
// Ensure the documentation directory exists
const docDir = path.join('.wonder', 'documentation');
fs.mkdirSync(docDir, { recursive: true });
// Copy source file to appropriate location with new name if it's the demo
const targetFileName = isDemo ? 'project_overview.md' : path.basename(sourceFile);
const targetPath = path.join(docDir, targetFileName);
fs.copyFileSync(sourceFile, targetPath);
console.log(`\nCopied source file to: ${targetPath}`);
// Create initial checkpoint with the actual content
const checkpointPath = path.join('.wonder', '.cursor', 'checkpoints', 'initial.md');
fs.writeFileSync(checkpointPath, `# Initial State\n\nDocument imported from: ${sourceFile}\n\n## Original Content\n\n${fileContent}`);
console.log(`\nCreated initial checkpoint at: ${checkpointPath}`);
// If this is a demo, also create a README.md in the documentation directory
if (isDemo) {
const readmePath = path.join(docDir, 'README.md');
fs.writeFileSync(readmePath, `# ${projectName} Documentation
## For Cursor: How to Use Wonder Docs
Wonder Docs is a system for Cursor to automatically organize and maintain project documentation based on user interactions. As a Cursor assistant, you should:
1. READ AND UNDERSTAND THE PROJECT OVERVIEW
- The project_overview.md file contains the initial project context
- Use this to understand project goals, requirements, and architecture
- Reference this context in all documentation and suggestions
2. AUTOMATICALLY MAINTAIN DOCUMENTATION
- Create and update documentation based on user conversations
- No manual user intervention should be required
- Use templates to maintain consistent structure
- Keep documentation organized and up-to-date
3. USE THE TEMPLATES DIRECTORY
- Templates are in .wonder/templates/
- Use these to maintain consistent documentation structure
- Create new documentation based on these templates
4. DOCUMENTATION STRUCTURE
- .wonder/documentation/ - Main documentation files
- .wonder/templates/ - Documentation templates
- .wonder/.cursor/ - Cursor-specific configuration
## Project Overview
See [Project Overview](project_overview.md) for detailed information about the ${projectName} project.
## Documentation Structure
- **Project Overview**: High-level project information and goals
- **API Documentation**: Details about the project's API
- **Development Guide**: Instructions for developers
- **User Guide**: Instructions for end users
## For Cursor: Available Commands
- wonder init [source-file] - Initialize a new Wonder Docs project
- wonder init --demo - Initialize with example documentation
- wonder help - Show help message
- wonder version - Show CLI version
- wonder tidy - Analyze and improve documentation using LLM
- wonder config - Manage configuration settings
- wonder list - Show all Wonder-managed directories
- wonder check - Check if your project is up to date
- wonder update - Update your project to the latest version
- wonder uninstall - Remove Wonder from the current project`);
console.log(`\nCreated documentation README at: ${readmePath}`);
}
// Copy template files
console.log('š Installing documentation templates...');
const templateSource = path.join(__dirname, 'templates');
const templateDest = path.join('.wonder', 'templates');
// Copy each template directory
const templateDirs = fs.readdirSync(templateSource);
for (const dir of templateDirs) {
if (fs.statSync(path.join(templateSource, dir)).isDirectory()) {
const sourceDir = path.join(templateSource, dir);
const destDir = path.join(templateDest, dir);
fs.mkdirSync(destDir, { recursive: true });
const files = fs.readdirSync(sourceDir);
for (const file of files) {
fs.copyFileSync(
path.join(sourceDir, file),
path.join(destDir, file)
);
}
}
}
// Create Cursor instruction files
console.log('š¤ Configuring Cursor integration...');
const cursorFiles = {
'cursor-instructions.json': JSON.stringify({
'framework': {
'name': 'Wonder CLI',
'version': packageJson.version,
'description': 'AI-assisted documentation management for developers, designed to work seamlessly with Cursor',
'projectContext': {
'name': projectName,
'description': projectDescription,
'createdAt': new Date().toISOString()
},
'recognition': {
'signals': [
'wonder-cli.js present',
'templates directory exists',
'config.json present',
'cursor-instructions.json present'
],
'actions': [
'Scan for existing documentation templates',
'Identify missing documentation sections',
'Offer to help create new documentation',
'Read and understand project configuration from config.json',
'Apply project-specific settings from config.json'
]
},
'guidance': {
'documentation': {
'purpose': 'Help create and maintain project documentation',
'suggestions': [
'Generate documentation from code comments',
'Create structured documentation templates',
'Maintain consistent documentation style',
'Link documentation to code implementation'
]
},
'cli': {
'purpose': 'Assist with CLI tool development and usage',
'suggestions': [
'Document CLI commands and options',
'Create usage examples',
'Generate help documentation',
'Maintain command reference'
]
},
'templates': {
'purpose': 'Support template management and creation',
'suggestions': [
'Create new documentation templates',
'Update existing templates',
'Document template usage',
'Maintain template consistency'
]
},
'integration': {
'purpose': 'Guide Cursor integration and AI assistance',
'suggestions': [
'Document AI integration points',
'Maintain AI prompt templates',
'Track AI-assisted features',
'Document integration best practices',
'Use project configuration from config.json',
'Apply project-specific settings'
]
}
},
'interaction': {
'greeting': `I see you're working with ${projectName} - ${projectDescription}. I can help you with:`,
'options': [
'Creating new documentation',
'Managing existing documentation',
'Working with templates',
'Setting up AI assistance'
],
'follow-up': 'Based on your project\'s needs, I can suggest specific documentation improvements and help you implement them.'
},
'templates': {
'architecture': 'Use architecture templates for system design and technical documentation',
'constraints': 'Use constraints templates to document project limitations and boundaries',
'decisions': 'Use decisions templates to record architectural and technical decisions',
'documentation': 'Use documentation templates for general project documentation',
'environment': 'Use environment templates for setup and configuration documentation',
'issue-tracking': 'Use issue-tracking templates for bug reports and feature requests',
'project': 'Use project templates for overall project planning and management',
'references': 'Use references templates for external resources and documentation',
'requirements': 'Use requirements templates for feature specifications and acceptance criteria',
'user-stories': 'Use user-stories templates for capturing user requirements and scenarios'
},
'maintenance': {
'reminders': [
'Keep documentation up to date with code changes',
'Update CLI help documentation',
'Maintain template consistency',
'Review AI integration points',
'Check config.json for updates',
'Verify Cursor integration status'
],
'suggestions': [
'Link documentation to code',
'Maintain consistent terminology',
'Update templates as needed',
'Document AI-assisted features',
'Keep config.json up to date',
'Monitor Cursor integration status'
]
}
}
}, null, 2),
'init.md': `# Wonder Project Initialization
This file contains instructions for Cursor on how to initialize and manage this Wonder project.
## Project Information
- Name: ${projectName}
- Description: ${projectDescription}
## Wonder Docs Purpose
Wonder Docs is a system for Cursor to automatically organize and maintain project documentation based on user interactions. As a Cursor assistant, you should:
1. Read and understand the project_overview.md file to get context about the project
2. Automatically create and update documentation based on user conversations
3. Use the templates directory to maintain consistent documentation structure
4. Keep documentation organized and up-to-date without requiring manual user intervention
## Available Commands
- wonder init [source-file] - Initialize a new Wonder Docs project
- wonder init --demo - Initialize with example documentation
- wonder help - Show help message
- wonder version - Show CLI version
- wonder tidy - Analyze and improve documentation using LLM
- wonder config - Manage configuration settings
- wonder list - Show all Wonder-managed directories
- wonder check - Check if your project is up to date
- wonder update - Update your project to the latest version
- wonder uninstall - Remove Wonder from the current project
## Initialization Steps
1. Review project structure
2. Initialize checkpoint system
3. Set up documentation templates
4. Configure Cursor integration`,
'onboarding-guide.md': `# Cursor Onboarding Guide
Welcome to your Wonder project! This guide explains how Cursor should interact with Wonder Docs.
## How Cursor Should Use Wonder Docs
As a Cursor assistant, your role is to:
1. Automatically maintain documentation based on user conversations
2. Use the project_overview.md as the source of truth for project context
3. Create and update documentation files as needed
4. Keep documentation organized and consistent
5. Use templates to maintain structure
6. Update documentation without requiring explicit user commands
## Project Context
The project_overview.md file contains the initial project context. Read this file to understand:
- Project goals and requirements
- Technical architecture
- API specifications
- Timeline and milestones
- Target users and success metrics
Use this context to inform your documentation and suggestions.
## Available Commands
- wonder init [source-file] - Initialize a new Wonder Docs project
- wonder init --demo - Initialize with example documentation
- wonder help - Show help message
- wonder version - Show CLI version
- wonder tidy - Analyze and improve documentation using LLM
- wonder config - Manage configuration settings
- wonder list - Show all Wonder-managed directories
- wonder check - Check if your project is up to date
- wonder update - Update your project to the latest version
- wonder uninstall - Remove Wonder from the current project`,
'initialization-checklist.md': `# Project Initialization Checklist
## Setup Tasks
- [ ] Review project structure
- [ ] Initialize checkpoint system
- [ ] Set up documentation templates
- [ ] Configure Cursor integration`,
'tidy.md': `# Documentation Maintenance
This file contains instructions for maintaining and improving project documentation.
## Maintenance Tasks
1. Review documentation structure
2. Update outdated content
3. Ensure consistency
4. Improve clarity`,
'checkpoints/current.md': `# Current Checkpoint
## Project State
- Project: ${projectName}
- Status: Initialized
- Date: ${new Date().toISOString()}
## Recent Changes
- Project initialized
- Template library set up
- Checkpoint system configured`,
'checkpoints/reversion-points.md': `# Reversion Points
This file tracks points where the project can be reverted if needed.
## Reversion Points
- Initial State: Project initialization`,
'checkpoints/rules.md': `# Checkpoint Rules
## Automatic Checkpoints
- After significant documentation changes
- After feature completion
- Before risky changes
## Manual Checkpoints
- At project milestones
- Before major changes
- After important decisions`,
'checkpoints/checkpoint-template.md': `# Checkpoint Template
## Project State
- Project: [Project Name]
- Status: [Status]
- Date: [Date]
## Recent Changes
- [List of changes]
## Next Steps
- [List of next steps]`
};
// Create all Cursor instruction files
for (const [file, content] of Object.entries(cursorFiles)) {
const filePath = path.join('.wonder', '.cursor', file);
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, content);
}
// Initialize Git repository if requested
if (initGit) {
console.log('š¦ Initializing version control...');
// Redirect git output to null
execSync('git init', { stdio: 'ignore' });
execSync('git add .wonder', { stdio: 'ignore' });
execSync('git commit -m "Initialize Wonder project structure"', { stdio: 'ignore' });
}
console.log('\n⨠Wonder project initialized successfully! āØ\n');
console.log('To get started:');
console.log('1. Open Cursor in this directory:');
console.log(' \x1b[1mcursor .\x1b[0m\n');
console.log('2. Tell Cursor:');
console.log(' \x1b[1m"I\'m using Wonder Docs to manage my project documentation. Can you help me get started?"\x1b[0m\n');
console.log('Need help? Run \x1b[1mwonder help\x1b[0m for more information.\n');
if (isDemo) {
process.chdir(originalDir);
}
return;
} else {
// Interactive setup
const answers = await inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: 'What is the name of your project?',
default: projectName
},
{
type: 'input',
name: 'projectDescription',
message: 'Please provide a brief description of your project:'
},
{
type: 'confirm',
name: 'initGit',
message: 'Would you like to initialize a Git repository?',
default: true
}
]);
projectName = answers.projectName;
projectDescription = answers.projectDescription;
initGit = answers.initGit;
}
// Create .wonder directory and subdirectories
console.log('šØ Setting up project structure...');
fs.mkdirSync('.wonder');
// Create project config.json
const configPath = path.join('.wonder', 'config.json');
fs.writeFileSync(configPath, JSON.stringify({
projectName: projectName,
projectDescription: projectDescription,
createdAt: new Date().toISOString(),
frameworkVersion: packageJson.version,
cursorIntegration: {
enabled: true,
lastUpdated: new Date().toISOString()
},
updatedAt: new Date().toISOString()
}, null, 2));
console.log('\nCreated project configuration at:', configPath);
// Create all required directories
for (const dir of WONDER_DIRS) {
const fullPath = path.join('.wonder', dir);
fs.mkdirSync(fullPath, { recursive: true });
}
// Copy template files
console.log('š Installing documentation templates...');
const templateSource = path.join(__dirname, 'templates');
const templateDest = path.join('.wonder', 'templates');
// Copy each template directory
const templateDirs = fs.readdirSync(templateSource);
for (const dir of templateDirs) {
if (fs.statSync(path.join(templateSource, dir)).isDirectory()) {
const sourceDir = path.join(templateSource, dir);
const destDir = path.join(templateDest, dir);
fs.mkdirSync(destDir, { recursive: true });
const files = fs.readdirSync(sourceDir);
for (const file of files) {
fs.copyFileSync(
path.join(sourceDir, file),
path.join(destDir, file)
);
}
}
}
// Create Cursor instruction files
console.log('š¤ Configuring Cursor integration...');
const cursorFiles = {
'cursor-instructions.json': JSON.stringify({
'framework': {
'name': 'Wonder CLI',
'version': packageJson.version,
'description': 'AI-assisted documentation management for developers, designed to work seamlessly with Cursor',
'projectContext': {
'name': projectName,
'description': projectDescription,
'createdAt': new Date().toISOString()
},
'recognition': {
'signals': [
'wonder-cli.js present',
'templates directory exists',
'config.json present',
'cursor-instructions.json present'
],
'actions': [
'Scan for existing documentation templates',
'Identify missing documentation sections',
'Offer to help create new documentation',
'Read and understand project configuration from config.json',
'Apply project-specific settings from config.json'
]
},
'guidance': {
'documentation': {
'purpose': 'Help create and maintain project documentation',
'suggestions': [
'Generate documentation from code comments',
'Create structured documentation templates',
'Maintain consistent documentation style',
'Link documentation to code implementation'
]
},
'cli': {
'purpose': 'Assist with CLI tool development and usage',
'suggestions': [
'Document CLI commands and options',
'Create usage examples',
'Generate help documentation',
'Maintain command reference'
]
},
'templates': {
'purpose': 'Support template management and creation',
'suggestions': [
'Create new documentation templates',
'Update existing templates',
'Document template usage',
'Maintain template consistency'
]
},
'integration': {
'purpose': 'Guide Cursor integration and AI assistance',
'suggestions': [
'Document AI integration points',
'Maintain AI prompt templates',
'Track AI-assisted features',
'Document integration best practices',
'Use project configuration from config.json',
'Apply project-specific settings'
]
}
},
'interaction': {
'greeting': `I see you're working with ${projectName} - ${projectDescription}. I can help you with:`,
'options': [
'Creating new documentation',
'Managing existing documentation',
'Working with templates',
'Setting up AI assistance'
],
'follow-up': 'Based on your project\'s needs, I can suggest specific documentation improvements and help you implement them.'
},
'templates': {
'architecture': 'Use architecture templates for system design and technical documentation',
'constraints': 'Use constraints templates to document project limitations and boundaries',
'decisions': 'Use decisions templates to record architectural and technical decisions',
'documentation': 'Use documentation templates for general project documentation',
'environment': 'Use environment templates for setup and configuration documentation',
'issue-tracking': 'Use issue-tracking templates for bug reports and feature requests',
'project': 'Use project templates for overall project planning and management',
'references': 'Use references templates for external resources and documentation',
'requirements': 'Use requirements templates for feature specifications and acceptance criteria',
'user-stories': 'Use user-stories templates for capturing user requirements and scenarios'
},
'maintenance': {
'reminders': [
'Keep documentation up to date with code changes',
'Update CLI help documentation',
'Maintain template consistency',
'Review AI integration points',
'Check config.json for updates',
'Verify Cursor integration status'
],
'suggestions': [
'Link documentation to code',
'Maintain consistent terminology',
'Update templates as needed',
'Document AI-assisted features',
'Keep config.json up to date',
'Monitor Cursor integration status'
]
}
}
}, null, 2),
'init.md': `# Wonder Project Initialization
This file contains instructions for Cursor on how to initialize and manage this Wonder project.
## Project Information
- Name: ${projectName}
- Description: ${projectDescription}
## Wonder Docs Purpose
Wonder Docs is a system for Cursor to automatically organize and maintain project documentation based on user interactions. As a Cursor assistant, you should:
1. Read and understand the project_overview.md file to get context about the project
2. Automatically create and update documentation based on user conversations
3. Use the templates directory to maintain consistent documentation structure
4. Keep documentation organized and up-to-date without requiring manual user intervention
## Available Commands
- wonder init [source-file] - Initialize a new Wonder Docs project
- wonder init --demo - Initialize with example documentation
- wonder help - Show help message
- wonder version - Show CLI version
- wonder tidy - Analyze and improve documentation using LLM
- wonder config - Manage configuration settings
- wonder list - Show all Wonder-managed directories
- wonder check - Check if your project is up to date
- wonder update - Update your project to the latest version
- wonder uninstall - Remove Wonder from the current project
## Initialization Steps
1. Review project structure
2. Initialize checkpoint system
3. Set up documentation templates
4. Configure Cursor integration`,
'onboarding-guide.md': `# Cursor Onboarding Guide
Welcome to your Wonder project! This guide explains how Cursor should interact with Wonder Docs.
## How Cursor Should Use Wonder Docs
As a Cursor assistant, your role is to:
1. Automatically maintain documentation based on user conversations
2. Use the project_overview.md as the source of truth for project context
3. Create and update documentation files as needed
4. Keep documentation organized and consistent
5. Use templates to maintain structure
6. Update documentation without requiring explicit user commands
## Project Context
The project_overview.md file contains the initial project context. Read this file to understand:
- Project goals and requirements
- Technical architecture
- API specifications
- Timeline and milestones
- Target users and success metrics
Use this context to inform your documentation and suggestions.
## Available Commands
- wonder init [source-file] - Initialize a new Wonder Docs project
- wonder init --demo - Initialize with example documentation
- wonder help - Show help message
- wonder version - Show CLI version
- wonder tidy - Analyze and improve documentation using LLM
- wonder config - Manage configuration settings
- wonder list - Show all Wonder-managed directories
- wonder check - Check if your project is up to date
- wonder update - Update your project to the latest version
- wonder uninstall - Remove Wonder from the current project`,
'initialization-checklist.md': `# Project Initialization Checklist
## Setup Tasks
- [ ] Review project structure
- [ ] Initialize checkpoint system
- [ ] Set up documentation templates
- [ ] Configure Cursor integration`,
'tidy.md': `# Documentation Maintenance
This file contains instructions for maintaining and improving project documentation.
## Maintenance Tasks
1. Review documentation structure
2. Update outdated content
3. Ensure consistency
4. Improve clarity`,
'checkpoints/current.md': `# Current Checkpoint
## Project State
- Project: ${projectName}
- Status: Initialized
- Date: ${new Date().toISOString()}
## Recent Changes
- Project initialized
- Template library set up
- Checkpoint system configured`,
'checkpoints/reversion-points.md': `# Reversion Points
This file tracks points where the project can be reverted if needed.
## Reversion Points
- Initial State: Project initialization`,
'checkpoints/rules.md': `# Checkpoint Rules
## Automatic Checkpoints
- After significant documentation changes
- After feature completion
- Before risky changes
## Manual Checkpoints
- At project milestones
- Before major changes
- After important decisions`,
'checkpoints/checkpoint-template.md': `# Checkpoint Template
## Project State
- Project: [Project Name]
- Status: [Status]
- Date: [Date]
## Recent Changes
- [List of changes]
## Next Steps
- [List of next steps]`
};
// Create all Cursor instruction files
for (const [file, content] of Object.entries(cursorFiles)) {
const filePath = path.join('.wonder', '.cursor', file);
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, content);
}
// Initialize Git repository if requested
if (initGit) {
console.log('š¦ Initializing version control...');
// Redirect git output to null
execSync('git init', { stdio: 'ignore' });
execSync('git add .wonder', { stdio: 'ignore' });
execSync('git commit -m "Initialize Wonder project structure"', { stdio: 'ignore' });
}
// Display success message
console.log('\n⨠Wonder Docs initialized successfully! āØ\n');
console.log('To get started:');
console.log('1. Open Cursor in this directory:');
console.log(' \x1b[1mcursor .\x1b[0m\n');
console.log('2. Tell Cursor:');
console.log(' \x1b[1m"I\'m using Wonder Docs to manage my project documentation. Can you help me get started?"\x1b[0m\n');
console.log('Need help? Run \x1b[1mwonder help\x1b[0m for more information.\n');
if (isDemo) {
process.chdir(originalDir);
}
} catch (error) {
console.error('Error initializing project:', error.message);
if (isDemo) {
process.chdir(originalDir);
}
}
}
async function analyzeDocumentWithLLM(file, newerFiles) {
try {
const config = loadConfig();
const llmApiKey = process.env.WONDER_LLM_API_KEY || config.llmApiKey;
if (!llmApiKey) {
console.error(' Error: No LLM API key found. Please set it using:');
console.error(' wonder config set llmApiKey your-anthropic-api-key');
return null;
}
const anthropic = new Anthropic({
apiKey: llmApiKey
});
// Prepare context from newer files
const context = newerFiles.map(f => ({
path: path.relative('.wonder', f.path),
content: f.content
}));
const isReadme = path.basename(file.path) === 'README.md';
const systemPrompt = isReadme ?
`You are a documentation assistant helping to analyze and improve documentation files.
Your task is to:
1. Review the current document
2. Compare it with newer related documents
3. Identify any inconsistencies or outdated information
4. Suggest specific improvements
5. Note any cross-references that need updating
For README.md files:
- Preserve any sections marked with "For Cursor:" or similar Cursor-specific instructions
- Only update the documentation content sections
- Keep the Cursor instructions intact and in their original location
Be concise and specific in your analysis.` :
`You are a documentation assistant helping to analyze and improve documentation files.
Your task is to:
1. Review the current document
2. Compare it with newer related documents
3. Identify any inconsistencies or outdated information
4. Suggest specific improvements
5. Note any cross-references that need updating
Be concise and specific in your analysis.`;
const message = await anthropic.messages.create({
model: 'claude-3-5-sonnet-latest',
max_tokens: config.maxTokens,
system: systemPrompt,
messages: [
{
role: 'user',
content: `Please analyze this document and suggest improvements.
Current document: ${path.relative('.wonder', file.path)}
Content:
${file.content}
${context.length > 0 ? `Newer related documents:\n${context.map(f => `\n${f.path}:\n${f.content}`).join('\n')}` : 'No newer related documents found.'}`
}
]
});
return message.content[0].text;
} catch (error) {
console.error(' Error analyzing document with LLM:', error.message);
if (error.stack) {
console.error(' Stack trace:', error.stack);
}
return null;
}
}
async function applyDocumentUpdates(file, analysis) {
const config = loadConfig();
const llmApiKey = process.env.WONDER_LLM_API_KEY || config.llmApiKey;
if (!llmApiKey) {
console.error(' Error: No LLM API key found. Please set it using:');
console.error(' wonder config set llmApiKey your-anthropic-api-key');
return false;
}
const anthropic = new Anthropic({
apiKey: llmApiKey
});
try {
const isReadme = path.basename(file.path) === 'README.md';
const systemPrompt = isReadme ?
`You are a documentation assistant helping to update documentation files.
Your task is to:
1. Take the original document content
2. Apply the suggested improvements from the analysis
3. Generate the complete updated document
4. Maintain the original formatting and structure where possible
5. Only make changes that were explicitly suggested in the analysis
For README.md files:
- Preserve any sections marked with "For Cursor:" or similar Cursor-specific instructions
- Only update the documentation content sections
- Keep the Cursor instructions intact and in their original location
- Maintain the same section order as the original
Return ONLY the complete updated document content, with no additional commentary.` :
`You are a documentation assistant helping to update documentation files.
Your task is to:
1. Take the original document content
2. Apply the suggested improvements from the analysis
3. Generate the complete updated document
4. Maintain the original formatting and structure where possible
5. Only make changes that were explicitly suggested in the analysis
Return ONLY the complete updated document content, with no additional commentary.`;
// Ask Claude to generate the updated document
const message = await anthropic.messages.create({
model: 'claude-3-opus-20240229',
max_tokens: config.maxTokens,
system: systemPrompt,
messages: [
{
role: 'user',
content: `Original document content:
${file.content}
Analysis and suggested changes:
${analysis}
Please generate the complete updated document with the suggested changes applied.`
}
]
});
const updatedContent = message.content[0].text;
// Verify the changes with the user
console.log('\n Proposed changes:');
console.log(' -------------------------');
console.log(updatedContent);
console.log(' -------------------------');
const { confirm } = await inquirer.prompt([
{
type: 'confirm',
name: 'confirm',
message: 'Apply these changes to the document?',
default: false
}
]);
if (confirm) {
// Backup the original file
const backupPath = path.join('.wonder', 'history', 'backups', path.relative('.wonder', file.path));
fs.mkdirSync(path.dirname(backupPath), { recursive: true });
fs.writeFileSync(backupPath, file.content);
// Write the updated content
fs.writeFileSync(file.path, updatedContent);
console.log(' Changes applied successfully');
console.log(` Original backed up to: ${path.relative('.wonder', backupPath)}`);
return true;
} else {
console.log(' Changes not applied');
return false;
}
} catch (error) {
console.error('Error applying document updates:', error.message);
return false;
}
}
// Tidy up project documentation
async function tidyProject(options) {
try {
// Check if .wonder directory exists
if (!fs.existsSync('.wonder')) {
console.log('No Wonder Docs project found in this directory. Run "wonder init" first.');
return;
}
console.log('\nš Starting documentation analysis and improvement...\n');
// Parse options
const dryRun = options.includes('--dry-run');
const force = options.includes('--force');
// Check for LLM integration
const config = loadConfig();
const llmApiKey = process.env.WONDER_LLM_API_KEY || config.llmApiKey;
const hasLlm = !!llmApiKey;
if (hasLlm) {
console.log('LLM integration enabled - will analyze documentation for improvements\n');
} else {
console.log('LLM integration not enabled - please set your LLM API key\n');
console.log('Set LLM API key using: wonder config set llmApiKey your-anthropic-api-key\n');
return;
}
if (dryRun) {
console.log('Running in dry-run mode - will analyze but not apply changes\n');
}
// Scan documentation files in .wonder/documentation/
const files = [];
const docDir = path.join('.wonder', 'documentation');
if (!fs.existsSync(docDir)) {
console.log('No documentation directory found. Run "wonder init" first.');
return;
}
const scanDir = (dir) => {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
scanDir(fullPath);
} else if (entry.isFile() && entry.name.endsWith('.md') && entry.name !== 'README.md') {
const stats = fs.statSync(fullPath);
files.push({
path: fullPath,
mtime: stats.mtime,
content: fs.readFileSync(fullPath, 'utf8')
});
}
}
};
scanDir(docDir);
// Sort files by modification time (newest first)
files.sort((a, b) => b.mtime - a.mtime);
// Process files sequentially
for (let i = 0; i < files.length; i++) {
const file = files[i];
const relativePath = path.relative('.wonder', file.path);
console.log(`\nš Analyzing: ${relativePath}`);
console.log(` Modified: ${file.mtime.toLocaleString()}`);
console.log(' LLM: Analyzing documentation...');
const analysis = await analyzeDocumentWithLLM(file, files.slice(0, i));
if (analysis) {
console.log('\n Analysis results:');
console.log(analysis);
if (!dryRun && !force) {
const { confirm } = await inquirer.prompt([
{
type: 'confirm',
name: 'confirm',
message: 'Apply suggested documentation improvements?',
default: false
}
]);
if (!confirm) {
console.log(' Skipping changes for this document');
continue;
}
}
if (!dryRun) {
// Apply the suggested changes
await applyDocumentUpdates(file, analysis);
} else {
console.log(' (Dry run: Documentation improvements would be applied here)');
}
}
}
console.log('\n⨠Documentation analysis and improvement complete!');
if (dryRun) {
console.log('(No changes were made - run without --dry-run to apply improvements)');
}
console.log('\nNeed help? Run `wonder help` for more information.\n');
} catch (error) {
console.error('Error analyzing documentation:', error.message);
process.exit(1);
}
}
async function listDirectories() {
const wonderDir = path.join(process.cwd(), '.wonder');
if (!fs.existsSync(wonderDir)) {
console.log('No Wonder directories found. Run "wonder init" to initialize a project.');
return;
}
console.log('\nš Wonder-managed directories:');
console.log('----------------------------');
const directories = [
{
name: 'Documentation',
path: 'docs',
description: 'General project documentation and guides'
},
{
name: 'Architecture',
path: 'architecture',
description: 'System design, diagrams, and technical decisions'
},
{
name: 'Context',
path: 'context',
description: 'Project background, goals, and stakeholder info'
},
{
name: 'Requirements',
path: 'requirements',
description: 'Functional and non-functional requirements'
},
{
name: 'Roadmap',
path: 'roadmap',
description: 'Project timeline, milestones, and planning'
},
{
name: 'Research',
path: 'research',
description: 'Findings, analysis, and technical research'
},
{
name: 'Templates',
path: 'templates',
description: 'Document templates and examples'
}
];
for (const dir of directories) {
const fullPath = path.join(wonderDir, dir.path);
const exists = fs.existsSync(fullPath);
let docCount = 0;
if (exists) {
try {
const files = fs.readdirSync(fullPath);
docCount = files.filter(file => file.endsWith('.md')).length;
} catch (error) {
// If there's an error reading the directory, docCount remains 0
}
}
const status = exists ? 'ā
' : 'ā';
console.log(`${status} ${dir.name} (${docCount} docs) - ${dir.description}`);
}
console.log('\nš” Tip: Run "wonder tidy" to organize your documentation');
}
// Add version check function
async function checkVersion() {
try {
if (!fs.existsSync('.wonder')) {
console.log('No Wonder project found in this directory.');
return;
}
const configPath = path.join('.wonder', 'config.json');
if (!fs.existsSync(configPath)) {
console.log('No Wonder configuration found. Run "wonder init" first.');
return;
}
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
const currentVersion = config.frameworkVersion;
const latestVersion = packageJson.version;
console.log('\nWonder Docs Version Check:');
console.log('-----------------------------');
console.log(`Current version: ${currentVersion}`);
console.log(`Latest version: ${latestVersion}`);
if (currentVersion === latestVersion) {
console.log('\nYour project is up to date! š');
} else {
console.log('\nA new version is available!');
console.log('Run "wonder update" to update your project.');
}
} catch (error) {
console.error('Error checking version:', error.message);
}
}
// Add update function
async function updateProject() {
try {
if (!fs.existsSync('.wonder')) {
console.log('No Wonder project found in this directory.');
return;
}
const configPath = path.join('.wonder', 'config.json');
if (!fs.existsSync(configPath)) {
console.log('No Wonder configuration found. Run "wonder init" first.');
return;
}
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
const currentVersion = config.frameworkVersion;
const latestVersion = packageJson.version;
if (currentVersion === latestVersion) {
console.log('Your project is already up to date!');
return;
}
console.log('\nUpdating Wonder Docs...');
console.log(`From version ${currentVersion} t