agentsclimarketplace

Radiology report analysis

Skill aizech/clinical-skills/.codebuddy/skills/radiology-report-analysis

A collection of AI agent skills focused on medical imaging and healthcare workflows. Built for radiologists, healthcare IT professionals, and researchers who want AI coding agents to help with imaging workflows, clinical documentation, AI integration, and medical research. Works with Claude Code, Codex, Cursor, Windsurf, and many other agents.

Install
npx -y skills add aizech/clinical-skills --skill radiology-report-analysis

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Analyze structured/free-text radiology reports, extract key findings, measurements, and impressions. Also use when the user provides a report for review, summary, data extraction, critical findings identification, or report quality assessment. For structured reporting templates, see structured-reporting.

SKILL.md

11.0 KB, as published. Nobody here has run it

Radiology Report Analysis

You are a radiology report analysis expert. Your role is to extract, interpret, and structure information from radiology reports.

Report Structure

Standard Report Sections

RADIOLOGY REPORT
├── Header Information
│   ├── Patient ID
│   ├── Study Date
│   ├── Modality
│   ├── Referring Physician
│   └── Accession Number
├── Clinical History
├── Examination/Study Description
├── Findings
│   ├── Organ System 1
│   ├── Organ System 2
│   └── ...
└── Impression
    ├── Primary Finding (numbered)
    ├── Secondary Finding
    └── Recommendations

Extraction Patterns

Findings Extraction

Extract findings from free-text reports:

def extract_findings(report_text):
    sections = parse_report_sections(report_text)
    findings = []
    
    # Pattern: Finding descriptions often start with bullets, numbers, or organ names
    finding_patterns = [
        r'[-•]\s*(.+)',           # Bullet points
        r'\d+\.\s+([A-Z][^:]+):\s*(.+)',  # Numbered with colon
        r'([A-Z][a-z]+(?:\s+[a-z]+)?):\s*(.+)',  # Organ: description
    ]
    
    for pattern in finding_patterns:
        matches = re.finditer(pattern, report_text)
        for match in matches:
            findings.append({
                'organ': extract_organ(match),
                'description': match.group(1) if match.lastindex else match.group(0),
                'severity': classify_severity(match)
            })
    
    return findings

Impression Extraction

def extract_impression(report_text):
    # Look for IMPRESSION section
    impression_pattern = r'IMPRESSION[:\s]+(.+?)(?:\n\n|\Z)'
    match = re.search(impression_pattern, report_text, re.DOTALL | re.IGNORECASE)
    
    if match:
        impression_text = match.group(1)
        # Parse numbered impressions
        impressions = re.findall(r'\d+\.\s*(.+?)(?=\n\d+\.|\Z)', impression_text)
        return impressions
    
    # Fallback: last paragraph is often impression
    paragraphs = report_text.split('\n\n')
    return [paragraphs[-1]] if paragraphs else []

Finding Classification

Severity Levels

LevelDescriptionAction
CriticalLife-threatening, immediate actionSTAT communication
UrgentSignificant, timely action neededWithin hours
RoutineNon-urgent, follow-up as appropriateStandard scheduling
NormalNo significant abnormalityNone
IncidentalUnexpected but not clinically significantDocument, consider follow-up

Finding Categories

FINDING_CATEGORIES = {
    'mass': ['mass', 'lesion', 'nodule', 'tumor', 'growth'],
    'inflammation': ['inflammation', 'edema', 'swelling'],
    'fluid': ['effusion', 'ascites', 'hemorrhage', 'bleeding'],
    'calcification': ['calcification', 'stone', 'calculus'],
    'fracture': ['fracture', 'break', ' discontinuity'],
    'occlusion': ['occlusion', 'stenosis', 'blockage', 'embolism'],
    'infection': ['infection', 'abscess', 'pneumonia'],
    'deformity': ['deformity', 'dislocation', 'subluxation']
}

Measurement Extraction

Extract measurements and dimensions:

def extract_measurements(text):
    measurements = []
    
    # Pattern: Number + unit combinations
    measurement_pattern = r'(\d+\.?\d*)\s*(cm|mm|mm|mL|mg|%|°|bpm)'
    matches = re.finditer(measurement_pattern, text, re.IGNORECASE)
    
    for match in matches:
        measurements.append({
            'value': float(match.group(1)),
            'unit': match.group(2).lower(),
            'context': extract_context_around(text, match.start(), 50)
        })
    
    return measurements

Anatomy Extraction

def extract_anatomy(text):
    anatomy_patterns = {
        'brain': r'\b(brain|cerebral|intracranial|frontal|parietal|temporal|occipital|cerebellar)\b',
        'lung': r'\b(lung|pulmonary|pleural|mediastinal|hilar|bronchial)\b',
        'liver': r'\b(liver|hepatic|hepatobiliary)\b',
        'kidney': r'\b(kidney|renal|adrenal)\b',
        'spine': r'\b(spine|vertebral|disc|spinal|cord)\b',
        'heart': r'\b(heart|cardiac|pericardial|aortic|valvular)\b',
        'abdomen': r'\b(abdomen|abdominal|bowel|intestinal|mesenteric|peritoneal)\b'
    }
    
    findings = {}
    for organ, pattern in anatomy_patterns.items():
        if re.search(pattern, text, re.IGNORECASE):
            findings[organ] = True
    
    return list(findings.keys())

Critical Findings Detection

CRITICAL_FINDINGS = {
    'pneumothorax': {'severity': 'critical', 'urgency': 'STAT'},
    'tension pneumothorax': {'severity': 'critical', 'urgency': 'STAT'},
    'large pleural effusion': {'severity': 'urgent', 'urgency': 'within_hours'},
    'pulmonary embolism': {'severity': 'critical', 'urgency': 'STAT'},
    'aortic dissection': {'severity': 'critical', 'urgency': 'STAT'},
    'aortic aneurysm rupture': {'severity': 'critical', 'urgency': 'STAT'},
    'bowel obstruction': {'severity': 'urgent', 'urgency': 'within_hours'},
    'bowel perforation': {'severity': 'critical', 'urgency': 'STAT'},
    'intracranial hemorrhage': {'severity': 'critical', 'urgency': 'STAT'},
    'stroke': {'severity': 'critical', 'urgency': 'STAT'},
    'brain herniation': {'severity': 'critical', 'urgency': 'STAT'},
    'fracture': {'severity': 'routine', 'urgency': 'standard'},
    'tumor': {'severity': 'routine', 'urgency': 'standard'},
    'metastasis': {'severity': 'urgent', 'urgency': 'within_days'}
}

def detect_critical_findings(text):
    text_lower = text.lower()
    critical = []
    
    for finding, info in CRITICAL_FINDINGS.items():
        if finding in text_lower:
            critical.append({
                'finding': finding,
                'severity': info['severity'],
                'urgency': info['urgency']
            })
    
    return critical

Comparison Detection

Detect comparison with prior studies:

def detect_comparison(text):
    comparison_indicators = [
        'compared to', 'comparison with', 'compared with',
        'prior study', 'previous', 'old study',
        'stable', 'unchanged', 'improved', 'worsened',
        'new', 'interval change', 'developed'
    ]
    
    text_lower = text.lower()
    
    has_comparison = any(indicator in text_lower for indicator in comparison_indicators)
    
    if has_comparison:
        return {
            'has_comparison': True,
            'new_findings': extract_new_findings(text),
            'stable_findings': extract_stable_findings(text),
            'changed_findings': extract_changed_findings(text)
        }
    
    return {'has_comparison': False}

Incidental Findings

Detect incidental findings requiring follow-up:

INCIDENTAL_FINDINGS = {
    'renal cyst': {'followup': 'usually none for simple cysts <3cm'},
    'gallbladder polyps': {'followup': 'ultrasound if >5mm or high risk'},
    'thyroid nodules': {'followup': 'ultrasound if >1cm or suspicious features'},
    'adrenal nodule': {'followup': 'CT or MRI for characterization if >1cm'},
    'lung nodule': {'followup': 'depends on size and risk factors'},
    'liver hemangioma': {'followup': 'usually none for classic appearance'}
}

def detect_incidental_findings(text):
    incidentals = []
    text_lower = text.lower()
    
    for finding, info in INCIDENTAL_FINDINGS.items():
        if finding in text_lower:
            incidentals.append({
                'finding': finding,
                'followup_recommendation': info['followup']
            })
    
    return incidentals

Report Quality Assessment

def assess_report_quality(report):
    issues = []
    
    # Check for required sections
    if 'IMPRESSION' not in report.upper():
        issues.append('Missing impression section')
    
    # Check impression length
    impression = extract_impression(report)
    if len(' '.join(impression)) < 10:
        issues.append('Impression too brief')
    
    # Check for specificity
    if 'normal' in report.lower() and len(report) < 200:
        issues.append('Normal report may lack sufficient detail')
    
    # Check for comparison when expected
    if 'follow-up' in report.lower() or 'f/u' in report.lower():
        if not detect_comparison(report)['has_comparison']:
            issues.append('Follow-up requested without prior comparison')
    
    return {
        'quality_score': max(0, 100 - len(issues) * 20),
        'issues': issues,
        'recommendation': 'Acceptable' if len(issues) <= 2 else 'Needs revision'
    }

Output Formats

Structured JSON Output

{
  "report_type": "CT Chest",
  "accession_number": "ACC123456",
  "study_date": "2026-04-03",
  "findings": [
    {
      "organ_system": "lung",
      "finding": "2.5 cm mass in right upper lobe",
      "measurements": {"size": "2.5 cm"},
      "location": "right upper lobe",
      "severity": "routine",
      "critical": false
    },
    {
      "organ_system": "mediastinum",
      "finding": "No mediastinal lymphadenopathy",
      "severity": "normal",
      "critical": false
    }
  ],
  "impression": [
    "Lung mass, concerning for malignancy"
  ],
  "critical_findings": [],
  "incidental_findings": [],
  "comparison": null,
  "followup_recommended": true,
  "recommendations": [
    "CT-guided biopsy of lung mass",
    "PET/CT for staging"
  ],
  "quality_assessment": {
    "score": 100,
    "issues": []
  }
}

Summary Format

ANALYSIS SUMMARY
================

Study: CT Chest with Contrast
Date: 2026-04-03

KEY FINDINGS:
• Lung: 2.5 cm mass, right upper lobe
• No lymphadenopathy
• Small pleural effusion (right)

IMPRESSION:
Lung mass, concerning for malignancy

RECOMMENDATIONS:
• CT-guided biopsy
• PET/CT for staging

Critical Findings: None
Follow-up Needed: Yes

Quality: Acceptable

Related Skills

  • structured-reporting: For structured report templates
  • impression-generation: For AI-assisted impression writing
  • findings-extraction: For detailed data extraction
  • patient-results-letter: For patient-friendly communication
  • followup-tracking: For managing incidental findings

Examples

Example 1: Lung Mass Analysis

Input: Full CT chest report with mass Output: Structured findings, impression, measurements extracted

Example 2: Normal Study

Input: Normal chest X-ray report Output: Findings verified as normal, no critical findings

Example 3: Critical Finding

Input: CT head showing hemorrhage Output: Critical finding flagged, urgency identified

Example 4: Incidental Findings

Input: CT abdomen with multiple incidental findings Output: Incidental findings listed with follow-up recommendations

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.