claudes-office
Version:
CLI tool to initialize Claude's office in your project
347 lines (331 loc) ⢠15.4 kB
JavaScript
"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