brainstorming
Structured design dialogue that validates ideas before implementation begins.
twinmind-core-workflow-a
Execute TwinMind primary workflow: Meeting transcription and summary generation. Use when implementing meeting capture, building transcription features, or automating meeting documentation. Trigger with phrases like "twinmind transcription workflow", "meeting transcription", "capture meeting with...
Full skill instructions
Primary workflow for capturing meetings, generating transcripts, and creating AI summaries.
twinmind-install-auth setup// src/workflows/meeting-capture.ts
import { getTwinMindClient } from '../twinmind/client';
import { Transcript, Summary } from '../twinmind/types';
interface MeetingOptions {
title?: string;
calendarEventId?: string;
language?: string;
enableDiarization?: boolean;
}
export class MeetingCapture {
private client = getTwinMindClient();
async startLiveCapture(options: MeetingOptions = {}): Promise<string> {
const response = await this.client.post('/meetings/live/start', {
title: options.title || `Meeting ${new Date().toISOString()}`,
calendar_event_id: options.calendarEventId,
language: options.language || 'auto',
diarization: options.enableDiarization ?? true,
model: 'ear-3',
});
return response.data.session_id;
}
async stopCapture(sessionId: string): Promise<Transcript> {
const response = await this.client.post(`/meetings/live/${sessionId}/stop`);
return response.data.transcript;
}
async transcribeRecording(audioUrl: string, options: MeetingOptions = {}): Promise<Transcript> {
const response = await this.client.post('/transcribe', {
audio_url: audioUrl,
title: options.title,
language: options.language || 'auto',
diarization: options.enableDiarization ?? true,
model: 'ear-3',
});
return this.waitForTranscript(response.data.transcript_id);
}
private async waitForTranscript(transcriptId: string, maxWaitMs = 300000): Promise<Transcript> {
const startTime = Date.now();
const pollIntervalMs = 2000;
while (Date.now() - startTime < maxWaitMs) {
const response = await this.client.get(`/transcripts/${transcriptId}`);
if (response.data.status === 'completed') {
return response.data;
}
if (response.data.status === 'failed') {
throw new Error(`Transcription failed: ${response.data.error}`);
}
await new Promise(r => setTimeout(r, pollIntervalMs));
}
throw new Error('Transcription timeout');
}
}
// src/workflows/summary-generation.ts
export interface SummaryOptions {
format?: 'brief' | 'detailed' | 'bullet-points';
includeActionItems?: boolean;
includeKeyPoints?: boolean;
maxLength?: number;
}
export class SummaryGenerator {
private client = getTwinMindClient();
async generateSummary(
transcriptId: string,
options: SummaryOptions = {}
): Promise<Summary> {
const response = await this.client.post('/summarize', {
transcript_id: transcriptId,
format: options.format || 'detailed',
include_action_items: options.includeActionItems ?? true,
include_key_points: options.includeKeyPoints ?? true,
max_length: options.maxLength || 500,
});
return response.data;
}
async generateFollowUpEmail(transcriptId: string): Promise<string> {
const response = await this.client.post('/generate/follow-up-email', {
transcript_id: transcriptId,
});
return response.data.email_content;
}
async generateMeetingNotes(transcriptId: string): Promise<string> {
const response = await this.client.post('/generate/meeting-notes', {
transcript_id: transcriptId,
format: 'markdown',
});
return response.data.notes;
}
}
// src/workflows/speaker-handling.ts
export interface Speaker {
id: string;
name?: string;
email?: string;
speakingTime: number;
segments: number;
}
export class SpeakerManager {
async identifySpeakers(transcript: Transcript, attendees?: string[]): Promise<Speaker[]> {
const speakers = new Map<string, Speaker>();
for (const segment of transcript.segments) {
const speakerId = segment.speaker_id || 'unknown';
const existing = speakers.get(speakerId);
if (existing) {
existing.speakingTime += segment.end - segment.start;
existing.segments += 1;
} else {
speakers.set(speakerId, {
id: speakerId,
speakingTime: segment.end - segment.start,
segments: 1,
});
}
}
// Match speakers to attendees if provided
if (attendees && attendees.length > 0) {
// TwinMind can match speakers to calendar attendees
const matched = await this.matchToAttendees(
Array.from(speakers.values()),
attendees
);
return matched;
}
return Array.from(speakers.values());
}
private async matchToAttendees(speakers: Speaker[], attendees: string[]): Promise<Speaker[]> {
const client = getTwinMindClient();
const response = await client.post('/speakers/match', {
speakers: speakers.map(s => s.id),
attendees,
});
return speakers.map((speaker, idx) => ({
...speaker,
name: response.data.matches[idx]?.name,
email: response.data.matches[idx]?.email,
}));
}
}
// src/workflows/full-meeting-workflow.ts
import { MeetingCapture } from './meeting-capture';
import { SummaryGenerator } from './summary-generation';
import { SpeakerManager } from './speaker-handling';
export interface MeetingResult {
transcriptId: string;
transcript: Transcript;
summary: Summary;
speakers: Speaker[];
followUpEmail?: string;
meetingNotes?: string;
}
export async function processMeeting(
audioUrl: string,
options: {
title?: string;
attendees?: string[];
generateEmail?: boolean;
generateNotes?: boolean;
} = {}
): Promise<MeetingResult> {
const capture = new MeetingCapture();
const summaryGen = new SummaryGenerator();
const speakerMgr = new SpeakerManager();
// Step 1: Transcribe
console.log('Starting transcription...');
const transcript = await capture.transcribeRecording(audioUrl, {
title: options.title,
enableDiarization: true,
});
console.log(`Transcription complete: ${transcript.id}`);
// Step 2: Generate summary (parallel with speaker identification)
console.log('Generating summary and identifying speakers...');
const [summary, speakers] = await Promise.all([
summaryGen.generateSummary(transcript.id, {
format: 'detailed',
includeActionItems: true,
includeKeyPoints: true,
}),
speakerMgr.identifySpeakers(transcript, options.attendees),
]);
const result: MeetingResult = {
transcriptId: transcript.id,
transcript,
summary,
speakers,
};
// Step 3: Optional outputs
if (options.generateEmail) {
result.followUpEmail = await summaryGen.generateFollowUpEmail(transcript.id);
}
if (options.generateNotes) {
result.meetingNotes = await summaryGen.generateMeetingNotes(transcript.id);
}
return result;
}
// Example usage
async function main() {
const result = await processMeeting(
'https://storage.example.com/meetings/standup-2025-01-15.mp3',
{
title: 'Daily Standup - Jan 15',
attendees: ['[email protected]', '[email protected]'],
generateEmail: true,
generateNotes: true,
}
);
console.log('Meeting processed successfully!');
console.log(`Summary: ${result.summary.summary}`);
console.log(`Action Items: ${result.summary.action_items.length}`);
console.log(`Speakers: ${result.speakers.map(s => s.name || s.id).join(', ')}`);
}
Example console output:
Starting transcription...
Transcription complete: tr_abc123
Generating summary and identifying speakers...
Meeting processed successfully!
Summary: Daily standup covering sprint progress and blockers...
Action Items: 3
Speakers: Alice, Bob, Charlie
| Error | Cause | Solution |
|---|---|---|
| Transcription timeout | Large audio file | Increase maxWaitMs or use async callback |
| Speaker match failed | No calendar data | Provide attendees list manually |
| Summary generation failed | Transcript too short | Ensure minimum 30s of audio |
| Audio format unsupported | Wrong codec | Convert to MP3/WAV/M4A |
| Rate limit exceeded | Too many requests | Implement queue-based processing |
| Format | Extension | Supported | Notes |
|---|---|---|---|
| MP3 | .mp3 | Yes | Recommended |
| WAV | .wav | Yes | Best quality |
| M4A | .m4a | Yes | iOS recordings |
| WebM | .webm | Yes | Browser recordings |
| OGG | .ogg | Yes | Open format |
| FLAC | .flac | Yes | Lossless |
For action item extraction and follow-up automation, see twinmind-core-workflow-b.
Structured design dialogue that validates ideas before implementation begins.
Comprehensive design intelligence for web and mobile UI/UX across 10 technology stacks.
Comprehensive implementation plans for multi-step tasks, breaking down specs into bite-sized, testable steps.
Introduction to the obra skills system with mandatory skill invocation rules and best practices.
Execute a written implementation plan with critical review and task checkpoints.
Delegate independent tasks to specialized agents working concurrently with isolated context.
Isolated git worktrees with smart directory selection and safety verification.
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
Plan searchable and shareable content that drives traffic, builds authority, and generates leads.
README-first repository scanner that extracts commands and classifies reproduction candidates without executing them.
Brainstorm and prioritize marketing strategies tailored to your SaaS stage, budget, and goals.
Plan and optimize your website's page hierarchy, navigation, URL structure, and internal linking.
The world's fastest calendar for remote work
Capture, organize, and utilize your knowledge effortlessly.
Instant Summaries of Audio & Video Interviews with AnySummary
Transform PDFs into engaging mind maps.
Chat with any PDF instantly
Create an optimal daily plan using your voice
A productivity platform to centralize organizational knowledge and workflows with contextual AI assistance.
AskYourPDF Pricing Plans: Tailored to Your Needs