Skill: eternal0404/eternal-meeting-extractor (version 1.0.0)

--- name: meeting-extractor description: Extract action items, decisions, and key points from meeting transcripts or notes. Use when the user has meeting notes, call transcripts, or recorded meeting text and wants: action items extracted, decisions highlighted, summary generated, or follow-up tasks identified. Triggers on "summarize meeting", "extract action items", "meeting notes", "what were the decisions", "meeting recap". --- # Meeting Extractor Parse meeting transcripts into structured summaries with action items. ## Usage ```bash python3 scripts/meeting.py extract transcript.txt python3 scripts/meeting.py extract --format json transcript.txt python3 scripts/meeting.py extract --followup transcript.txt ``` ## What It Extracts - **Summary** — 3-5 sentence overview - **Decisions** — Agreed-upon choices - **Action Items** — Tasks with owners and deadlines - **Key Points** — Important discussion topics - **Questions** — Unresolved questions needing answers - **Attendees** — Who participated ## Output Formats - Text (default) — Human-readable summary - JSON — Machine-readable for task systems - Markdown — For documentation/wiki

path: scripts/meeting.py
content: #!/usr/bin/env python3
"""Meeting Extractor — Pull action items and decisions from transcripts."""
import sys, re, json, argparse
from pathlib import Path

ACTION_PATTERNS = [
    r'(?:action|todo|task|assign|follow[- ]?up)[:\s]+(.+)',
    r'(\w+)\s+(?:will|should|needs? to|has to|going to)\s+(.+)',
    r'(?:by|before|deadline|due)\s+((?:monday|tuesday|wednesday|thursday|friday|saturday|sunday|tomorrow|next week|eod|eow|\d{1,2}/\d{1,2}|\w+ \d{1,2})\b.*)',
]

DECISION_PATTERNS = [
    r'(?:decided|agreed|approved|confirmed|chose|selected|going with)[:\s]+(.+)',
    r'(?:the decision is|we\'ll go with|final answer is)[:\s]+(.+)',
    r'(?:yes|approved|let\'s do it|sounds good|ship it)[.!]?\s*(.*)',
]

QUESTION_PATTERNS = [
    r'(\?.+)',
    r'(?:what about|how about|what if|why don\'t we|can we|should we)\s+(.+)\??',
]

def extract_attendees(text):
    """Extract attendee names from common patterns."""
    names = set()
    for m in re.finditer(r'(?:hi|hello|hey),?\s+(\w+)', text, re.I):
        names.add(m.group(1).title())
    for m in re.finditer(r'^(\w+):', text, re.M):
        word = m.group(1)
        if len(word) > 1 and word[0].isupper() and not word.isupper():
            names.add(word)
    return sorted(names)

def extract_actions(text):
    """Extract action items."""
    items = []
    for line in text.split('\n'):
        line = line.strip()
        if not line:
            continue
        for pat in ACTION_PATTERNS:
            m = re.search(pat, line, re.I)
            if m:
                items.append(line[:200])
                break
    return items

def extract_decisions(text):
    """Extract decisions."""
    items = []
    for line in text.split('\n'):
        line = line.strip()
        if not line:
            continue
        for pat in DECISION_PATTERNS:
            m = re.search(pat, line, re.I)
            if m:
                items.append(line[:200])
                break
    return items

def extract_questions(text):
    """Extract unresolved questions."""
    items = []
    for line in text.split('\n'):
        line = line.strip()
        if '?' in line and len(line) > 10:
            items.append(line[:200])
    return items

def summarize(text):
    """Create a brief summary from first/last parts."""
    sentences = re.split(r'[.!]\s+', text)
    sentences = [s.strip() for s in sentences if len(s.strip()) > 20]
    if len(sentences) <= 5:
        return ' '.join(sentences)
    return ' '.join(sentences[:3] + ['...'] + sentences[-2:])

def format_output(text, fmt='text'):
    attendees = extract_attendees(text)
    actions = extract_actions(text)
    decisions = extract_decisions(text)
    questions = extract_questions(text)
    summary = summarize(text)

    if fmt == 'json':
        return json.dumps({
            'summary': summary,
            'attendees': attendees,
            'action_items': actions,
            'decisions': decisions,
            'questions': questions,
        }, indent=2)

    lines = [f"\n{'='*50}", "  MEETING SUMMARY", f"{'='*50}", ""]

    if attendees:
        lines.append(f"  Attendees: {', '.join(attendees)}")
        lines.append("")

    lines.extend(["  Summary:", f"  {summary}", ""])

    if decisions:
        lines.append("  Decisions:")
        for i, d in enumerate(decisions, 1):
            lines.append(f"    {i}. {d}")
        lines.append("")

    if actions:
        lines.append("  Action Items:")
        for i, a in enumerate(actions, 1):
            lines.append(f"    {i}. {a}")
        lines.append("")

    if questions:
        lines.append("  Open Questions:")
        for i, q in enumerate(questions, 1):
            lines.append(f"    {i}. {q}")

    lines.append(f"\n{'='*50}")
    return '\n'.join(lines)

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('action', choices=['extract'])
    parser.add_argument('file', help='Transcript file')
    parser.add_argument('--format', choices=['text', 'json', 'markdown'], default='text')
    parser.add_argument('--followup', action='store_true', help='Generate follow-up email')
    args = parser.parse_args()

    text = Path(args.file).read_text()
    print(format_output(text, args.format))

if __name__ == '__main__':
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)