UNPKG

claudes-office

Version:

CLI tool to initialize Claude's office in your project

347 lines (331 loc) • 15.4 kB
"use strict"; /** * Meeting command for claudes-office * This command generates AI-powered meetings with transcripts and minutes */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.executeMeeting = executeMeeting; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const chalk = __importStar(require("chalk")); const inquirer = __importStar(require("inquirer")); const ora_1 = __importDefault(require("ora")); const commands_1 = require("../types/commands"); const generator_1 = require("../meetings/generator"); /** * Generate a meeting with the specified options * @param options - Meeting generation options */ async function executeMeeting(options = {}) { const debug = (message) => { if (options.debug) { console.log(chalk.gray(`[DEBUG] ${message}`)); } }; try { debug('Starting meeting generation with options: ' + JSON.stringify(options)); // Welcome message console.log(chalk.bold.cyan('\nšŸŽÆ Claude\'s Office Meeting Generator šŸŽÆ\n')); console.log(chalk.white('This tool will generate an AI-powered meeting with transcript and minutes.')); // Get current directory const currentDir = process.cwd(); debug(`Current directory: ${currentDir}`); // Check if office directory exists const officeDirName = 'claudes-office'; const officeDir = path.join(currentDir, officeDirName); if (!await fs.pathExists(officeDir)) { console.log(chalk.red.bold('\nāŒ Error: Office not found in this directory.')); console.log(chalk.yellow('Please run `claudes-office init` first to create the office.')); return; } // Check if meetings directory exists const meetingsDir = path.join(officeDir, 'meetings'); // Create meetings directory if it doesn't exist if (!await fs.pathExists(meetingsDir)) { await fs.mkdirp(meetingsDir); await fs.mkdirp(path.join(meetingsDir, 'transcripts')); await fs.mkdirp(path.join(meetingsDir, 'minutes')); // Create README.md for meetings directory const readmePath = path.join(meetingsDir, 'README.md'); const readmeContent = `# Meetings Directory This directory contains AI-generated meeting transcripts and minutes. ## Structure - \`/transcripts\`: Contains full, detailed meeting transcripts - \`/minutes\`: Contains concise meeting minutes with decisions and action items - \`/templates\`: Contains meeting prompt templates for different meeting types ## Usage To generate a new meeting, use the \`claudes-office meeting\` command with appropriate options. Example: \`\`\` claudes-office meeting --type=planning --title="Q3 Planning Meeting" --participants="Alice, Bob, Charlie" \`\`\` You can also use custom templates by creating them in the \`templates\` directory. `; await fs.writeFile(readmePath, readmeContent); // Create templates directory and example template const templatesDir = path.join(meetingsDir, 'templates'); await fs.mkdirp(templatesDir); const exampleTemplatePath = path.join(templatesDir, 'example.md'); const exampleTemplateContent = `# Custom Meeting Template Example You can create custom meeting templates in this directory. ## Meeting Information - Title: {{title}} - Type: {{type}} - Participants: {{participants}} - Duration: {{duration}} minutes - Date: {{date}} ## Agenda {{agenda}} ## Additional Context {{additionalContext}} # Instructions 1. Create a realistic meeting focused on [specific purpose] 2. Include [specific content or structure elements] 3. Make sure to capture [specific outcomes] # Output Format Generate your response in the following format: ## Transcript [Create a detailed, realistic transcript of the entire meeting] ## Minutes [Create concise meeting minutes that capture key points, decisions, and action items] ## Summary [Provide a brief 2-3 sentence summary of the meeting outcomes] `; await fs.writeFile(exampleTemplatePath, exampleTemplateContent); } // Interactive mode if options are not provided if (!options.type || !options.title) { // Get meeting type const { meetingType } = await inquirer.prompt([ { type: 'list', name: 'meetingType', message: 'Select the type of meeting:', choices: [ { name: '🧠 Brainstorming', value: commands_1.MeetingType.BRAINSTORM }, { name: 'šŸ“‹ Planning', value: commands_1.MeetingType.PLANNING }, { name: '🚩 Standup', value: commands_1.MeetingType.STANDUP }, { name: 'šŸ”„ Retrospective', value: commands_1.MeetingType.RETROSPECTIVE }, { name: 'āœ… Decision Making', value: commands_1.MeetingType.DECISION }, { name: 'šŸ“ Requirements Gathering', value: commands_1.MeetingType.REQUIREMENTS }, { name: 'šŸŽØ Custom', value: commands_1.MeetingType.CUSTOM } ], default: commands_1.MeetingType.PLANNING } ]); options.type = meetingType; // Get meeting title const { title } = await inquirer.prompt([ { type: 'input', name: 'title', message: 'Enter the meeting title:', default: `${meetingType.charAt(0).toUpperCase() + meetingType.slice(1)} Meeting`, validate: (input) => { if (!input.trim()) { return 'Please enter a meeting title'; } return true; } } ]); options.title = title; // Get participants const { participants } = await inquirer.prompt([ { type: 'input', name: 'participants', message: 'Enter the meeting participants (comma-separated):', default: 'Alice, Bob, Charlie', validate: (input) => { if (!input.trim()) { return 'Please enter at least one participant'; } return true; } } ]); options.participants = participants.split(',').map(p => p.trim()); // Get agenda items const { hasAgenda } = await inquirer.prompt([ { type: 'confirm', name: 'hasAgenda', message: 'Do you want to specify agenda items?', default: true } ]); if (hasAgenda) { const { agendaItems } = await inquirer.prompt([ { type: 'editor', name: 'agendaItems', message: 'Enter the meeting agenda (one item per line):', default: '1. Introduction and objectives\n2. Main discussion points\n3. Action items and next steps' } ]); // Parse agenda items from the editor (one item per line) options.agenda = agendaItems.split('\n') .map(line => line.trim()) .filter(line => line); } // Get meeting duration const { duration } = await inquirer.prompt([ { type: 'number', name: 'duration', message: 'Enter the meeting duration (in minutes):', default: 60, validate: (input) => { if (isNaN(input) || input <= 0) { return 'Please enter a positive number for the duration'; } return true; } } ]); options.duration = duration; } // Set default values for missing options if (!options.participants) { options.participants = ['Alice', 'Bob', 'Charlie']; } else if (typeof options.participants === 'string') { options.participants = [options.participants]; } if (!options.agenda && options.agenda !== '') { // Using agenda array instead of string to fix type issue const agendaItems = []; switch (options.type) { case commands_1.MeetingType.BRAINSTORM: agendaItems.push('Idea generation', 'Discussion and refinement', 'Next steps'); break; case commands_1.MeetingType.PLANNING: agendaItems.push('Project status', 'Timeline review', 'Resource allocation', 'Action items'); break; case commands_1.MeetingType.STANDUP: agendaItems.push('Updates from team members', 'Blockers and issues', 'Today\'s priorities'); break; case commands_1.MeetingType.RETROSPECTIVE: agendaItems.push('What went well', 'What could be improved', 'Action items for next iteration'); break; case commands_1.MeetingType.DECISION: agendaItems.push('Review options', 'Discuss pros and cons', 'Make decision', 'Implementation plan'); break; case commands_1.MeetingType.REQUIREMENTS: agendaItems.push('Stakeholder needs', 'Functional requirements', 'Non-functional requirements', 'Prioritization'); break; default: agendaItems.push('Introduction', 'Main discussion points', 'Action items'); } options.agenda = agendaItems; } // Check if SMART_MODEL environment variable is set if (!process.env.SMART_MODEL) { console.log(chalk.yellow.bold('\nāš ļø Warning: SMART_MODEL environment variable is not set.')); console.log(chalk.yellow('The default model (Claude) will be used for meeting generation.')); console.log(chalk.yellow('You can set SMART_MODEL to specify a different model.')); console.log(chalk.yellow('Example: export SMART_MODEL=anthropic:claude-3-opus-20240229')); console.log(chalk.yellow('Supported providers: anthropic, openai, local\n')); } // Check if API key is set if (!process.env.ANTHROPIC_API_KEY && !process.env.OPENAI_API_KEY) { console.log(chalk.red.bold('\nāŒ Error: No API key found for any supported model.')); console.log(chalk.yellow('Please set one of the following environment variables:')); console.log(chalk.yellow('- ANTHROPIC_API_KEY for Anthropic Claude models')); console.log(chalk.yellow('- OPENAI_API_KEY for OpenAI models')); return; } // Set output directory const outputDir = options.output || path.join(officeDirName, 'meetings'); // Generate meeting const generationSpinner = (0, ora_1.default)({ text: chalk.cyan(`Generating ${options.type} meeting...`), color: 'cyan', spinner: 'dots' }).start(); debug('Generating meeting with options: ' + JSON.stringify({ ...options, type: options.type, title: options.title, participants: options.participants, agenda: options.agenda, duration: options.duration, output: outputDir })); try { const result = await (0, generator_1.generateMeeting)({ type: options.type, title: options.title, participants: options.participants, agenda: options.agenda, duration: options.duration, output: outputDir }); generationSpinner.succeed(chalk.green('Meeting generated successfully!')); // Display result console.log(chalk.magenta.bold('\nšŸ“‹ Meeting Information:')); console.log(chalk.white(`Title: ${options.title}`)); console.log(chalk.white(`Type: ${options.type}`)); console.log(chalk.white(`Participants: ${options.participants.join(', ')}`)); console.log(chalk.white(`Duration: ${options.duration} minutes`)); console.log(chalk.magenta.bold('\nšŸ“„ Generated Files:')); console.log(chalk.white(`Transcript: ${result.transcriptPath}`)); console.log(chalk.white(`Minutes: ${result.minutesPath}`)); console.log(chalk.magenta.bold('\nšŸ“Œ Meeting Summary:')); console.log(chalk.white(result.summary)); console.log(chalk.cyan.bold('\n✨ Meeting generation completed!')); } catch (error) { generationSpinner.fail(chalk.red('Meeting generation failed')); console.error(chalk.red(`Error: ${error.message}`)); if (options.debug) { console.error(chalk.gray('Stack trace:'), error.stack); } } } catch (error) { console.error(chalk.red.bold('\nāŒ Error generating meeting:'), error.message); if (options.debug) { console.error(chalk.gray('Stack trace:'), error.stack); } process.exit(1); } } //# sourceMappingURL=meeting.js.map