| name | twinmind-core-workflow-b |
| description | Execute TwinMind secondary workflow: Action item extraction and follow-up automation.
Use when automating meeting follow-ups, extracting tasks,
or integrating with project management tools.
Trigger with phrases like "twinmind action items",
"meeting follow-up automation", "extract tasks from meeting".
|
| allowed-tools | Read, Write, Edit, Bash(npm:*), Grep |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
TwinMind Core Workflow B: Action Items & Follow-ups
Overview
Secondary workflow for extracting action items, automating follow-ups, and integrating with task management systems.
Prerequisites
- Completed
twinmind-core-workflow-a (transcription)
- Valid transcript or summary available
- Integration tokens for external services (optional)
Instructions
Step 1: Extract Action Items
import { getTwinMindClient } from '../twinmind/client';
export interface ActionItem {
id: string;
text: string;
assignee?: string;
dueDate?: string;
priority?: 'high' | 'medium' | 'low';
context?: string;
status: 'pending' | 'completed';
}
export interface ActionItemExtractionOptions {
includeContext?: boolean;
assignFromSpeakers?: boolean;
inferDueDates?: boolean;
maxItems?: number;
}
export class ActionItemExtractor {
private client = getTwinMindClient();
async extractFromTranscript(
transcriptId: string,
options: ActionItemExtractionOptions = {}
): Promise<ActionItem[]> {
const response = await this.client.post('/extract/action-items', {
transcript_id: transcriptId,
include_context: options.includeContext ?? true,
assign_from_speakers: options.assignFromSpeakers ?? true,
infer_due_dates: options.inferDueDates ?? true,
max_items: options.maxItems || 20,
});
return response.data.action_items.map((item: any) => ({
id: item.id,
text: item.text,
assignee: item.assignee,
dueDate: item.due_date,
priority: this.inferPriority(item),
context: item.context,
status: 'pending',
}));
}
private inferPriority(item: any): 'high' | 'medium' | 'low' {
const text = item.text.toLowerCase();
if (text.includes('urgent') || text.includes('asap') || text.includes('critical')) {
return 'high';
}
if (text.includes('important') || text.includes('soon')) {
return 'medium';
}
return 'low';
}
async categorizeItems(items: ActionItem[]): Promise<Map<string, ActionItem[]>> {
const categories = new Map<string, ActionItem[]>();
for (const item of items) {
const category = this.determineCategory(item);
if (!categories.has(category)) {
categories.set(category, []);
}
categories.get(category)!.push(item);
}
return categories;
}
private determineCategory(item: ActionItem): string {
const text = item.text.toLowerCase();
if (text.includes('review') || text.includes('check')) return 'Review';
if (text.includes('create') || text.includes('build') || text.includes('develop')) return 'Development';
if (text.includes('send') || text.includes('email') || text.includes('contact')) return 'Communication';
if (text.includes('schedule') || text.includes('meeting')) return 'Meetings';
if (text.includes('document') || text.includes('write')) return 'Documentation';
return 'General';
}
}
Step 2: Automate Follow-up Emails
export interface FollowUpEmailOptions {
recipients: string[];
includeTranscript?: boolean;
includeSummary?: boolean;
includeActionItems?: boolean;
customNote?: string;
sendImmediately?: boolean;
}
export interface GeneratedEmail {
to: string[];
cc?: string[];
subject: string;
body: string;
attachments?: Array<{
filename: string;
content: string;
contentType: string;
}>;
}
export class FollowUpAutomation {
private client = getTwinMindClient();
async generateFollowUp(
transcriptId: string,
options: FollowUpEmailOptions
): Promise<GeneratedEmail> {
const response = await this..(, {
: transcriptId,
: options.,
: options. ?? ,
: options. ?? ,
: options. ?? ,
: options.,
});
{
: options.,
: response..,
: response..,
: response..,
};
}
(: ): <{ : }> {
response = ..(, {
: email.,
: email.,
: email.,
: email.,
: email.,
});
{ : response.. };
}
(
: ,
:
): <{ : }> {
response = ..(, {
...email,
: sendAt.(),
});
{ : response.. };
}
}
Step 3: Integrate with Task Management
export interface TaskIntegration {
name: string;
createTask(item: ActionItem): Promise<string>;
updateTask(taskId: string, updates: Partial<ActionItem>): Promise<void>;
}
export class AsanaIntegration implements TaskIntegration {
name = 'Asana';
constructor(private accessToken: string, private projectId: string) {}
async createTask(item: ActionItem): Promise<string> {
const response = await fetch('https://app.asana.com/api/1.0/tasks', {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': ,
},
: .({
: {
: item.,
: item.,
: item.,
: [.],
: item.,
},
}),
});
data = response.();
data..;
}
(: , : <>): <> {
(, {
: ,
: {
: ,
: ,
},
: .({
: {
: updates.,
: updates. === ,
},
}),
});
}
}
{
name = ;
() {}
(: ): <> {
response = (, {
: ,
: {
: .,
: ,
},
: .({
: ,
: {
: {
: .,
: item.,
: item.,
: item.,
: .(item.),
},
},
}),
});
data = response.();
data....;
}
(?: ): {
(priority) {
: ;
: ;
: ;
: ;
}
}
(: , : <>): <> {
}
}
(): {
(service) {
:
(
process..!,
process..!
);
:
(
process..!,
process..!
);
:
();
}
}
Step 4: Complete Follow-up Workflow
import { ActionItemExtractor, ActionItem } from './action-items';
import { FollowUpAutomation } from './follow-up';
import { getTaskIntegration, TaskIntegration } from '../integrations/task-sync';
export interface FollowUpWorkflowOptions {
transcriptId: string;
attendees: string[];
taskIntegration?: 'asana' | 'linear' | 'notion' | 'jira';
sendEmail?: boolean;
emailDelay?: number;
}
export interface FollowUpResult {
actionItems: ActionItem[];
createdTasks: Array<{ item: ActionItem; externalId: string }>;
emailSent?: { messageId: string };
emailScheduled?: { scheduleId: string; sendAt: Date };
}
export async (): <> {
extractor = ();
followUp = ();
.();
actionItems = extractor.(options., {
: ,
: ,
: ,
});
.();
: = {
actionItems,
: [],
};
(options.) {
.();
integration = (options.);
( item actionItems) {
{
externalId = integration.(item);
result..({ item, externalId });
.();
} (error) {
.(, error);
}
}
}
(options. && options.. > ) {
email = followUp.(options., {
: options.,
: ,
: ,
});
(options. && options. > ) {
sendAt = (.() + options. * * );
result. = followUp.(email, sendAt);
result.. = sendAt;
.();
} {
result. = followUp.(email);
.();
}
}
result;
}
() {
result = ({
: ,
: [, ],
: ,
: ,
: ,
});
.();
.();
.();
(result.) {
.();
}
}
Output
- Extracted action items with assignees and due dates
- Tasks created in project management tool
- Follow-up email sent or scheduled
- Complete audit trail
Example console output:
Extracting action items...
Found 5 action items
Creating tasks in linear...
Created task: Review Q1 budget spreadsheet...
Created task: Schedule design review meeting...
Created task: Update project timeline document...
Follow-up email scheduled for 2025-01-15T15:30:00Z
=== Follow-up Workflow Complete ===
Action Items: 5
Tasks Created: 5
Email scheduled for: 2025-01-15T15:30:00Z
Error Handling
| Error | Cause | Solution |
|---|
| No action items found | Transcript too vague | Verify meeting had clear action items |
| Task creation failed | Invalid project/team ID | Check integration credentials |
| Email send failed | Invalid recipients | Verify email addresses |
| Assignee not found | Name mismatch | Map speakers to user accounts |
| Due date parsing failed | Ambiguous date | Use explicit date format |
Supported Integrations
| Service | Task Creation | Due Dates | Assignees | Priority |
|---|
| Asana | Yes | Yes | Yes | No |
| Linear | Yes | Yes | Yes | Yes |
| Notion | Yes | Yes | Yes | No |
| Jira | Yes | Yes | Yes | Yes |
| Trello | Yes | Yes | Yes | No |
| Monday.com | Yes | Yes | Yes | Yes |
Resources
Next Steps
For troubleshooting issues, see twinmind-common-errors.