Skill: parasharnagle/mindmap-generator (version 1.0.0)

--- name: mindmap-generator description: Generates visual mindmap images from conversations, goals, decisions, and daily priorities — delivered as PNG images viewable directly in Telegram. Use when asked to visualize, map out, or break down topics, priorities, decisions, meeting notes, or weekly reviews. metadata: {"openclaw": {"emoji": "🗺️", "requires": {"bins": ["node", "npx"]}}} --- # Mindmap Generator Skill You are a Chief of Staff agent with the ability to generate **visual mindmaps** and deliver them as **PNG images** directly inside Telegram messages. --- ## When to Activate Activate this skill when ANY of the following are true: - User explicitly asks for a mindmap, mind map, visual overview, or says "map out", "visualize", "break down visually" - User asks to see their day, week, or priorities in a visual format - User shares meeting notes or a voice transcript and asks for structure (note: meeting notes are optional — not every interaction will have them) - User is making a decision and would benefit from seeing pros/cons/risks mapped out - User asks to decompose goals, projects, or plans - During a morning briefing when the user has 3+ priorities to juggle - User says "I'm overwhelmed" or "there's too much going on" — proactively offer a mindmap **Do NOT activate** for simple lists of 1-2 items or when the user explicitly asks for text-only output. --- ## How to Generate the Mindmap ### Step 1: Extract Hierarchical Structure From the user's input (voice note, text message, calendar data, or memory context), extract a tree structure: - **Root node** = main topic, date, or decision question - **Level 1** = major categories (max 5-7 branches) - **Level 2** = details, subtasks, specifics (max 3-5 per branch) - **Level 3** = only if needed for complex topics (max 2-3 per branch) Keep it to **3 levels max** for readability. If content is deeper, summarize at level 3. ### Step 2: Write Mermaid Mindmap Syntax Format the structure using Mermaid's mindmap syntax. This is indentation-based: ``` mindmap root((Main Topic)) Category A Detail 1 Detail 2 Category B Detail 3 Detail 4 Category C Detail 5 ``` #### Shape Guide (use intentionally, not for every node) - `((text))` = circle — use for the **root node only** - `(text)` = rounded rectangle — use for **categories** - `[text]` = square — use for **action items** - `)text(` = cloud — use for **ideas or open questions** - `))text((` = bang/explosion — use for **urgent or blocked items** - `{{text}}` = hexagon — use for **decisions** - Plain text = default — use for **details and notes** #### Status Markers - Prefix with ✅ for completed items - Prefix with ⏳ for pending/in-progress items - Prefix with ❌ for blocked items - Prefix with ⚠️ for risks or warnings - Prefix with 💡 for ideas or suggestions ### Step 3: Render to PNG Run the rendering script to convert Mermaid syntax to a PNG image: ```bash # Save the mermaid content to a temp .mmd file echo "$MERMAID_CONTENT" > /tmp/mindmap_input.mmd # Render to PNG using mermaid-cli ./scripts/render_mindmap.sh /tmp/mindmap_input.mmd /tmp/mindmap_output.png ``` The script uses `mmdc` (mermaid-cli) with a custom theme configured for readability on mobile screens (Telegram). ### Step 4: Send via Telegram After rendering, send the PNG image to the user's Telegram chat: ```bash ./scripts/send_telegram_photo.sh /tmp/mindmap_output.png "Here's your mindmap 🗺️" "$CHAT_ID" ``` The image will appear **inline in the Telegram conversation** — no downloads, no links, no HTML files. --- ## Formatting Rules 1. **Root node** = always use circle shape `((text))` 2. **Max 4 levels deep** — if deeper, summarize 3. **Max 7 branches** from root — group if more 4. **Short labels** — max 5-6 words per node. Details go in sub-nodes, not long labels 5. **No special characters** in node text that break Mermaid: avoid `(`, `)`, `[`, `]`, `{`, `}` inside label text unless they are shape delimiters 6. **Use status markers** (✅ ⏳ ❌) when the content involves tasks or progress 7. **No Markdown inside nodes** — Mermaid mindmap doesn't support bold/italic inside nodes --- ## Output Behavior - **Always** send the mindmap as a PNG image in Telegram (viewable inline) - **Always** include a brief text summary before or after the image (1-2 sentences) - **Optionally** offer to regenerate with changes: "Want me to adjust anything on this map?" - If rendering fails, fall back to a **text-based tree** using Unicode box-drawing characters: ``` 📊 Today's Priorities ├── 🔴 Client Proposal (due 2pm) │ ├── Review pricing section │ └── Add case studies ├── 🟡 Team Standup (11am) │ └── Prep sprint update ├── 🟢 Follow up with Rajesh │ └── Send updated timeline └── 📋 Admin ├── Expense report └── Update project tracker ``` --- ## Example Scenarios ### Scenario 1: Morning Briefing User says: "What's my day look like?" Generate this mindmap: ``` mindmap root((Wednesday Feb 18)) (Meetings) [10am - Team Standup] [2pm - Client Review] [4pm - 1:1 with Priya] (Tasks) ))⚠️ Proposal due today(( [⏳ Review PR #342] [⏳ Update roadmap doc] (Follow-ups) [❌ Rajesh - SOW overdue 3 days] [⏳ Ankit - waiting on pricing] )Open Questions( )Timeline for Phase 2( )Budget approval status( ``` Send with message: "Good morning! Here's your Wednesday mapped out. The proposal is due today and Rajesh's SOW is 3 days overdue — those need attention first. 🗺️" ### Scenario 2: Decision Analysis User says: "Should I take on the Acme consulting project?" ``` mindmap root((Acme Consulting Decision)) (Pros) ₹12L revenue over 3 months Expands fintech portfolio Rajesh intro to their CTO (Cons) 40hrs/month commitment Overlaps with product launch Below usual rate by 15% ))Risks(( Scope creep - no fixed SOW yet Payment terms NET-60 Single point of contact leaving {{Past Precedent}} Similar deal with TechCorp Went 2x over timeline But led to 3 referrals )Decision Factors( Can you delegate product launch? Is the CTO intro worth the discount? What does your cash flow look like in Q2? ``` ### Scenario 3: Post-Meeting Action Map (when meeting notes are available) After a meeting transcript is available, generate: ``` mindmap root((Meeting - Rajesh - Feb 18)) {{Decisions Made}} Go with Vendor A Launch date March 15 Budget approved at ₹8L (Action Items) [You - Send SOW by Friday] [Rajesh - Review pricing by Wed] [Priya - Set up staging env] )Open Questions( )Phase 2 timeline TBD( )Need legal review on clause 4.2( (Context) Rajesh seemed hesitant on timeline Budget was originally ₹6L - pushed up ``` ### Scenario 4: Weekly Review ``` mindmap root((Week 7 Review)) (✅ Completed - 5) ✅ Client proposal submitted ✅ Sprint planning done ✅ Hired frontend dev ✅ Updated investor deck ✅ Fixed auth bug (⏳ Carried Forward - 2) ⏳ Blog post draft ⏳ Vendor evaluation (❌ Dropped - 1) ❌ Office space tour - deprioritized (Key Wins) Client signed 6-month extension New dev starts Monday ))Blockers(( Legal review delayed 5 days AWS costs spiking - need investigation ``` --- ## Dependencies - `@mermaid-js/mermaid-cli` (mmdc) — renders Mermaid syntax to PNG/SVG - `curl` — for Telegram Bot API calls - Node.js v18+ — runtime for mermaid-cli

path: README.md
content: # mindmap-generator skill

An OpenClaw skill that generates visual mindmaps from conversations, goals, decisions, and daily priorities — delivered as PNG images directly in Telegram.

## Features

- **Mermaid-based rendering** — uses `@mermaid-js/mermaid-cli` (mmdc) to render mindmaps to PNG
- **Telegram delivery** — sends mindmap images inline via Bot API `sendPhoto`
- **6 templates** — daily priorities, decision tree, weekly review, goal decomposition, project kickoff, meeting action map
- **Graceful fallback** — text-based Unicode tree when rendering fails
- **Meeting notes optional** — works with or without meeting transcripts

## Structure

```
mindmap-generator/
├── SKILL.md                    # Skill definition (required)
├── scripts/
│   ├── render_mindmap.sh       # Mermaid → PNG renderer
│   ├── send_telegram_photo.sh  # Telegram Bot API sender
│   └── generate_and_send.sh    # Full pipeline (stdin → render → send)
├── references/
│   ├── mermaid_mindmap_syntax.md
│   └── mindmap_best_practices.md
└── assets/templates/
    ├── daily_priorities.txt
    ├── decision_tree.txt
    ├── weekly_review.txt
    ├── goal_decomposition.txt
    ├── project_kickoff.txt
    └── meeting_action_map.txt
```

## Prerequisites

- Node.js v18+
- `@mermaid-js/mermaid-cli` (`npm install @mermaid-js/mermaid-cli`)
- `curl` (for Telegram API)
- Telegram Bot Token (set as `TELEGRAM_BOT_TOKEN`)

## Publishing to ClawHub

```bash
clawhub auth login
clawhub publish . --slug mindmap-generator --name "Mindmap Generator" --version 1.0.0 --tags latest
```

## License

MIT


path: assets/templates/daily_priorities.txt
content: mindmap
  root(({{DATE}} — Your Day))
    (Meetings)
      [{{MEETING_1_TIME}} - {{MEETING_1_TITLE}}]
      [{{MEETING_2_TIME}} - {{MEETING_2_TITLE}}]
      [{{MEETING_3_TIME}} - {{MEETING_3_TITLE}}]
    (Priority Tasks)
      )){{URGENT_TASK}}((
      [⏳ {{TASK_2}}]
      [⏳ {{TASK_3}}]
    (Follow-ups)
      [{{FOLLOWUP_1_STATUS}} {{FOLLOWUP_1}}]
      [{{FOLLOWUP_2_STATUS}} {{FOLLOWUP_2}}]
    )Open Items(
      ){{OPEN_QUESTION_1}}(
      ){{OPEN_QUESTION_2}}(


path: assets/templates/decision_tree.txt
content: mindmap
  root(({{DECISION_QUESTION}}))
    (Pros)
      {{PRO_1}}
      {{PRO_2}}
      {{PRO_3}}
    (Cons)
      {{CON_1}}
      {{CON_2}}
      {{CON_3}}
    ))Risks((
      {{RISK_1}}
      {{RISK_2}}
      {{RISK_3}}
    {{Past Precedent}}
      {{PRECEDENT_1}}
      {{PRECEDENT_2}}
    )Key Questions(
      ){{QUESTION_1}}(
      ){{QUESTION_2}}(
      ){{QUESTION_3}}(


path: assets/templates/goal_decomposition.txt
content: mindmap
  root(({{GOAL_PERIOD}} Goals))
    ({{GOAL_1_NAME}})
      [{{MILESTONE_1A}}]
      [{{MILESTONE_1B}}]
      [{{MILESTONE_1C}}]
    ({{GOAL_2_NAME}})
      [{{MILESTONE_2A}}]
      [{{MILESTONE_2B}}]
    ({{GOAL_3_NAME}})
      [{{MILESTONE_3A}}]
      [{{MILESTONE_3B}}]
      [{{MILESTONE_3C}}]
    ({{GOAL_4_NAME}})
      [{{MILESTONE_4A}}]
      [{{MILESTONE_4B}}]


path: assets/templates/meeting_action_map.txt
content: mindmap
  root((Meeting - {{PERSON}} - {{DATE}}))
    {{Decisions Made}}
      {{DECISION_1}}
      {{DECISION_2}}
      {{DECISION_3}}
    (Action Items)
      [{{OWNER_1}} - {{ACTION_1}} by {{DUE_1}}]
      [{{OWNER_2}} - {{ACTION_2}} by {{DUE_2}}]
      [{{OWNER_3}} - {{ACTION_3}} by {{DUE_3}}]
    )Open Questions(
      ){{OPEN_1}}(
      ){{OPEN_2}}(
    (Context Notes)
      {{NOTE_1}}
      {{NOTE_2}}


path: assets/templates/project_kickoff.txt
content: mindmap
  root(({{PROJECT_NAME}}))
    (Scope)
      {{SCOPE_ITEM_1}}
      {{SCOPE_ITEM_2}}
      {{SCOPE_ITEM_3}}
    (Stakeholders)
      {{STAKEHOLDER_1}}
      {{STAKEHOLDER_2}}
      {{STAKEHOLDER_3}}
    (Timeline)
      [{{PHASE_1}} - {{PHASE_1_DATE}}]
      [{{PHASE_2}} - {{PHASE_2_DATE}}]
      [{{PHASE_3}} - {{PHASE_3_DATE}}]
    ))Risks((
      {{RISK_1}}
      {{RISK_2}}
    (Tech Stack)
      {{TECH_1}}
      {{TECH_2}}
      {{TECH_3}}
    )Open Questions(
      ){{QUESTION_1}}(
      ){{QUESTION_2}}(


path: assets/templates/weekly_review.txt
content: mindmap
  root((Week {{WEEK_NUM}} Review))
    (✅ Completed)
      ✅ {{COMPLETED_1}}
      ✅ {{COMPLETED_2}}
      ✅ {{COMPLETED_3}}
    (⏳ Carried Forward)
      ⏳ {{CARRY_1}}
      ⏳ {{CARRY_2}}
    (❌ Dropped)
      ❌ {{DROPPED_1}}
    (Key Wins)
      {{WIN_1}}
      {{WIN_2}}
    ))Blockers((
      {{BLOCKER_1}}
      {{BLOCKER_2}}
    )Next Week Focus(
      ){{FOCUS_1}}(
      ){{FOCUS_2}}(


path: references/mermaid_mindmap_syntax.md
content: # Mermaid Mindmap Syntax — Quick Reference

> For the mindmap-generator OpenClaw skill. Full docs: https://mermaid.js.org/syntax/mindmap.html

---

## Basic Structure

Mermaid mindmaps use **indentation** to define hierarchy:

```
mindmap
  root((Central Topic))
    Branch A
      Leaf 1
      Leaf 2
    Branch B
      Leaf 3
```

- Each level is indented further than its parent
- The root node is the first node (typically uses circle shape)
- No connectors or arrows needed — hierarchy is implicit

---

## Node Shapes

| Syntax | Shape | Best Used For |
|---|---|---|
| `id((text))` | Circle | Root node |
| `id(text)` | Rounded rectangle | Categories, groups |
| `id[text]` | Square | Action items, tasks |
| `id)text(` | Cloud | Ideas, open questions |
| `id))text((` | Bang/explosion | Urgent items, blockers |
| `id{{text}}` | Hexagon | Decisions |
| `Plain text` | Default (rounded) | Details, notes |

---

## Icons

Add icons using `::icon()` after a node:

```
mindmap
  root((Project))
    Research
    ::icon(fa fa-book)
    Development
    ::icon(fa fa-code)
```

Requires Font Awesome or Material Design Icons to be available.

---

## CSS Classes

Add custom styling with `:::className`:

```
mindmap
  root((Project))
    Urgent Task
    :::urgent
    Normal Task
```

Classes must be defined by the site/application rendering the mindmap.

---

## Markdown Strings

Use backtick-wrapped strings for rich text:

```
mindmap
  root((Project))
    id1["`**Bold text** with
    multiple lines`"]
    id2["`Regular *italic* text`"]
```

---

## Layout Options

Default is radial layout. Tidy Tree is also available:

```
---
config:
  layout: tidy-tree
---
mindmap
  root((Topic))
    A
    B
    C
```

---

## Best Practices for Agent-Generated Mindmaps

1. **Keep labels short** — 3-6 words per node
2. **Max 7 branches** from root (Miller's Law)
3. **Max 3-4 levels** deep for mobile readability
4. **Use shapes intentionally** — don't make every node a different shape
5. **Root always circle** `(( ))` — visual anchor
6. **Avoid special characters** in node text: `(`, `)`, `[`, `]`, `{`, `}` break parsing unless they're shape delimiters
7. **Test rendering** — some emoji work in Mermaid, some don't. Stick to: ✅ ⏳ ❌ ⚠️ 💡
8. **Consistent indentation** — use 2 or 4 spaces, don't mix tabs

---

## Common Pitfalls

- **No colon in node text** — `Task: Do thing` may break. Use `Task - Do thing`
- **No parentheses in plain text** — `Review (draft)` breaks parsing. Use `Review draft`
- **Empty nodes** — every node needs text content
- **Inconsistent indentation** — Mermaid will guess parent, but may guess wrong


path: references/mindmap_best_practices.md
content: # Mindmap Best Practices for AI Personal Assistants

> Reference document for the mindmap-generator skill.
> Guides the agent on WHEN and HOW to create effective mindmaps.

---

## When Mindmaps Add Value (vs. Plain Text)

| Situation | Use Mindmap? | Why |
|---|---|---|
| 3+ priorities competing for attention | ✅ Yes | Visual hierarchy helps triage |
| Simple to-do list (1-3 items) | ❌ No | Text is faster, less overhead |
| Decision with multiple factors | ✅ Yes | Seeing pros/cons/risks spatially aids judgment |
| Meeting recap with action items | ✅ Only if 3+ owners/threads | Otherwise a bullet list is clearer |
| Goal decomposition | ✅ Yes | Shows dependencies and hierarchy |
| Single status update | ❌ No | Text message is appropriate |
| Weekly review | ✅ Yes | Comparing completed vs. pending is visual |
| "I'm overwhelmed" | ✅ Yes | Externalizing structure reduces cognitive load |

**Rule of thumb:** If the content has **hierarchy AND 3+ branches**, a mindmap helps. If it's flat or small, stick to text.

---

## Structure Guidelines

### The 7±2 Rule (Miller's Law)
- Root should have **5-7 branches** max
- If you have more, group them into categories first
- Each branch should have **3-5 children** max

### Depth Limits
- **Level 1:** Categories (Meetings, Tasks, Follow-ups)
- **Level 2:** Specific items (Client call at 2pm, Review proposal)
- **Level 3:** Details only when needed (Pricing section needs update)
- **Level 4:** Avoid — if you're here, the branch needs restructuring

### Label Length
- **Ideal:** 3-5 words per node
- **Maximum:** 8 words — beyond this, the mindmap becomes unreadable on mobile
- **Bad:** "Review the updated pricing proposal that Rajesh sent on Monday"
- **Good:** "Review Rajesh pricing proposal"

---

## Shape Usage Strategy

Don't use shapes randomly. Use them as a **visual language** the user learns over time:

| Shape | Meaning | When |
|---|---|---|
| Circle `(( ))` | Central topic | Always the root, only the root |
| Rounded rect `( )` | Category/group | For organizing branches |
| Square `[ ]` | Action item | Something that needs to be DONE |
| Cloud `) (` | Open/uncertain | Questions, ideas, unknowns |
| Bang `)) ((` | Urgent/blocked | Things that need immediate attention |
| Hexagon `{{ }}` | Decision point | When a choice needs to be made |

**Consistency matters more than variety.** The user should be able to glance at a mindmap and immediately know what needs action (squares), what's urgent (bangs), and what's uncertain (clouds).

---

## Telegram-Specific Considerations

### Image Readability
- Telegram displays photos at ~300-400px width in chat
- Users can tap to zoom, but the mindmap should be scannable at small size
- **Implication:** Fewer branches and shorter labels are critical

### Dark Mode
- ~40% of Telegram users use dark mode
- White background PNGs work in both light and dark mode
- Avoid light-colored text or low-contrast elements

### Caption Strategy
- Always pair the image with a **1-2 sentence caption**
- The caption should highlight the most actionable insight:
  - "Your proposal is due today and Rajesh's SOW is 3 days overdue — those need attention first."
  - NOT "Here's a mindmap of your day" (obvious, adds nothing)

### When the Image Fails
- Rendering can fail (Node.js not available, complex syntax, etc.)
- Always have a **text fallback** using Unicode tree characters:
  ```
  📊 Today's Priorities
  ├── 🔴 Client Proposal (due 2pm)
  │   ├── Review pricing
  │   └── Add case studies
  ├── 🟡 Standup (11am)
  └── 🟢 Follow up with Rajesh
  ```

---

## Meeting Notes: Optional Input

Meeting notes are one of many possible inputs. The skill should handle:

1. **Meeting notes available** → Extract decisions, actions, open questions → Mindmap
2. **No meeting notes** → Use calendar, memory, and messages instead → Mindmap
3. **Voice transcript** → Parse the unstructured text → Extract hierarchy → Mindmap
4. **Simple text request** → "Map out my Q3 goals" → Use goals from memory → Mindmap

**Never assume meeting notes exist.** The agent should gracefully work with whatever context is available.

---

## Progressive Complexity

Start simple, offer to elaborate:

1. **First version:** 2 levels, key categories only
2. **If user asks "expand on Tasks":** Add level 3 detail to that branch
3. **If user asks "add last week's context":** Pull from memory, add a "History" branch

This prevents overwhelming the user while keeping the option open for depth.


path: scripts/generate_and_send.sh
content: #!/usr/bin/env bash
# generate_and_send.sh — Full pipeline: Mermaid content → PNG → Telegram
#
# This is the main entry point the OpenClaw agent calls.
# It chains render + send into a single command.
#
# Usage: ./generate_and_send.sh <chat_id> [caption]
#
# Reads Mermaid mindmap syntax from STDIN.
#
# Example:
#   echo "mindmap
#     root((Today))
#       (Tasks)
#         Do the thing
#   " | ./generate_and_send.sh 123456789 "Here's your day!"
#
# Dependencies:
#   - Node.js v18+
#   - @mermaid-js/mermaid-cli
#   - curl
#   - TELEGRAM_BOT_TOKEN env var

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CHAT_ID="${1:?Usage: echo '<mermaid>' | generate_and_send.sh <chat_id> [caption]}"
CAPTION="${2:-🗺️ Here's your mindmap}"

# Generate unique temp filenames
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
INPUT_FILE="/tmp/mindmap_${TIMESTAMP}.mmd"
OUTPUT_FILE="/tmp/mindmap_${TIMESTAMP}.png"

# Read mermaid content from STDIN
if [[ -t 0 ]]; then
    echo "Error: No input provided. Pipe Mermaid mindmap syntax via STDIN." >&2
    echo "Example: echo 'mindmap\n  root((Topic))' | $0 $CHAT_ID" >&2
    exit 1
fi

cat > "$INPUT_FILE"

# Validate we got content
if [[ ! -s "$INPUT_FILE" ]]; then
    echo "Error: Empty input received" >&2
    exit 1
fi

echo "📝 Step 1/3: Received mindmap content ($(wc -l < "$INPUT_FILE" | tr -d ' ') lines)"

# Step 1: Render to PNG
echo "🎨 Step 2/3: Rendering mindmap to PNG..."
"$SCRIPT_DIR/render_mindmap.sh" "$INPUT_FILE" "$OUTPUT_FILE"

if [[ $? -ne 0 || ! -f "$OUTPUT_FILE" ]]; then
    echo "Error: Rendering failed. Sending text fallback to Telegram..." >&2

    # Fallback: send the raw content as a text message
    TEXT_CONTENT=$(cat "$INPUT_FILE")
    curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
        -F "chat_id=$CHAT_ID" \
        -F "text=⚠️ Couldn't render the mindmap as an image. Here's the structure:\n\n$TEXT_CONTENT" \
        -F "parse_mode=HTML" \
        > /dev/null 2>&1

    rm -f "$INPUT_FILE"
    exit 1
fi

# Step 2: Send to Telegram
echo "📱 Step 3/3: Sending to Telegram..."
"$SCRIPT_DIR/send_telegram_photo.sh" "$OUTPUT_FILE" "$CAPTION" "$CHAT_ID"

# Cleanup temp files
rm -f "$INPUT_FILE" "$OUTPUT_FILE"

echo "🎉 Done! Mindmap delivered to Telegram."


path: scripts/render_mindmap.sh
content: #!/usr/bin/env bash
# render_mindmap.sh — Renders Mermaid mindmap syntax to PNG image
#
# Usage: ./render_mindmap.sh <input.mmd> <output.png> [width] [height]
#
# Dependencies:
#   - Node.js v18+
#   - @mermaid-js/mermaid-cli (installed globally or via npx)
#
# The output PNG is optimized for Telegram viewing:
#   - Default 1200x800 for good readability on mobile
#   - White background (no transparency — looks clean in Telegram dark mode too)
#   - Scale factor 2x for retina displays

set -euo pipefail

INPUT_FILE="${1:?Usage: render_mindmap.sh <input.mmd> <output.png> [width] [height]}"
OUTPUT_FILE="${2:?Usage: render_mindmap.sh <input.mmd> <output.png> [width] [height]}"
WIDTH="${3:-1200}"
HEIGHT="${4:-800}"

# Validate input file exists
if [[ ! -f "$INPUT_FILE" ]]; then
    echo "Error: Input file not found: $INPUT_FILE" >&2
    exit 1
fi

# Validate input contains mindmap syntax
if ! grep -q "^mindmap" "$INPUT_FILE" 2>/dev/null; then
    echo "Warning: Input file may not contain valid Mermaid mindmap syntax" >&2
fi

# Create a Puppeteer config for mermaid-cli
# This ensures clean rendering with proper fonts
PUPPETEER_CONFIG=$(mktemp /tmp/mmdc-puppeteer-XXXXXX.json)
cat > "$PUPPETEER_CONFIG" <<'EOF'
{
    "headless": true,
    "args": [
        "--no-sandbox",
        "--disable-setuid-sandbox",
        "--disable-dev-shm-usage"
    ]
}
EOF

# Create mermaid config for styling
# Optimized for: readable on mobile, clean white background, good contrast
MERMAID_CONFIG=$(mktemp /tmp/mmdc-config-XXXXXX.json)
cat > "$MERMAID_CONFIG" <<EOF
{
    "theme": "default",
    "themeVariables": {
        "fontSize": "16px",
        "fontFamily": "arial, sans-serif"
    },
    "mindmap": {
        "padding": 20,
        "useMaxWidth": false
    }
}
EOF

# Render using mermaid-cli (mmdc)
# Priority: global install > local node_modules > npx auto-install
if command -v mmdc &> /dev/null; then
    MMDC_CMD="mmdc"
elif [[ -x "./node_modules/.bin/mmdc" ]]; then
    MMDC_CMD="./node_modules/.bin/mmdc"
elif [[ -d "/tmp/mmdc-test/node_modules" ]]; then
    # Use the local install we set up for testing
    MMDC_CMD="/tmp/mmdc-test/node_modules/.bin/mmdc"
else
    # Auto-install via npx as last resort
    MMDC_CMD="npx -y @mermaid-js/mermaid-cli"
fi

echo "Rendering mindmap: $INPUT_FILE → $OUTPUT_FILE"

$MMDC_CMD \
    -i "$INPUT_FILE" \
    -o "$OUTPUT_FILE" \
    -w "$WIDTH" \
    -H "$HEIGHT" \
    -s 2 \
    -b white \
    -c "$MERMAID_CONFIG" \
    -p "$PUPPETEER_CONFIG" \
    2>&1

RENDER_EXIT=$?

# Cleanup temp configs
rm -f "$PUPPETEER_CONFIG" "$MERMAID_CONFIG"

if [[ $RENDER_EXIT -ne 0 ]]; then
    echo "Error: Mermaid rendering failed with exit code $RENDER_EXIT" >&2
    exit $RENDER_EXIT
fi

# Verify output was created
if [[ ! -f "$OUTPUT_FILE" ]]; then
    echo "Error: Output file was not created: $OUTPUT_FILE" >&2
    exit 1
fi

FILE_SIZE=$(wc -c < "$OUTPUT_FILE" | tr -d ' ')
echo "✅ Mindmap rendered successfully: $OUTPUT_FILE ($FILE_SIZE bytes)"

# Telegram has a 10MB photo limit — warn if close
if [[ "$FILE_SIZE" -gt 9000000 ]]; then
    echo "⚠️  Warning: File is close to Telegram's 10MB photo limit" >&2
fi


path: scripts/send_telegram_photo.sh
content: #!/usr/bin/env bash
# send_telegram_photo.sh — Sends a PNG mindmap image to a Telegram chat
#
# Usage: ./send_telegram_photo.sh <image_path> <caption> <chat_id> [bot_token]
#
# The image appears INLINE in the Telegram conversation — no download required.
# Users see the mindmap directly in their chat, on mobile or desktop.
#
# Dependencies:
#   - curl
#   - TELEGRAM_BOT_TOKEN env var (or pass as 4th argument)
#
# Telegram Bot API limits:
#   - Photos: max 10MB
#   - Caption: max 1024 characters
#   - Supported formats: JPEG, PNG, GIF (PNG is best for mindmaps)

set -euo pipefail

IMAGE_PATH="${1:?Usage: send_telegram_photo.sh <image_path> <caption> <chat_id> [bot_token]}"
CAPTION="${2:?Usage: send_telegram_photo.sh <image_path> <caption> <chat_id> [bot_token]}"
CHAT_ID="${3:?Usage: send_telegram_photo.sh <image_path> <caption> <chat_id> [bot_token]}"
BOT_TOKEN="${4:-${TELEGRAM_BOT_TOKEN:?Set TELEGRAM_BOT_TOKEN env var or pass as 4th argument}}"

# Validate image exists
if [[ ! -f "$IMAGE_PATH" ]]; then
    echo "Error: Image file not found: $IMAGE_PATH" >&2
    exit 1
fi

# Validate image is a PNG or JPEG
FILE_EXT="${IMAGE_PATH##*.}"
FILE_EXT_LOWER=$(echo "$FILE_EXT" | tr '[:upper:]' '[:lower:]')
if [[ "$FILE_EXT_LOWER" != "png" && "$FILE_EXT_LOWER" != "jpg" && "$FILE_EXT_LOWER" != "jpeg" ]]; then
    echo "Warning: File is not PNG/JPEG ($FILE_EXT). Telegram may not display it inline." >&2
fi

# Check file size (Telegram limit: 10MB for photos)
FILE_SIZE=$(wc -c < "$IMAGE_PATH" | tr -d ' ')
if [[ "$FILE_SIZE" -gt 10485760 ]]; then
    echo "Error: File exceeds Telegram's 10MB photo limit ($FILE_SIZE bytes)" >&2
    exit 1
fi

# Truncate caption if too long (Telegram limit: 1024 chars)
if [[ ${#CAPTION} -gt 1024 ]]; then
    CAPTION="${CAPTION:0:1021}..."
    echo "Warning: Caption truncated to 1024 characters" >&2
fi

TELEGRAM_API="https://api.telegram.org/bot${BOT_TOKEN}/sendPhoto"

echo "Sending mindmap to Telegram chat $CHAT_ID..."

RESPONSE=$(curl -s -X POST "$TELEGRAM_API" \
    -F "chat_id=$CHAT_ID" \
    -F "photo=@$IMAGE_PATH" \
    -F "caption=$CAPTION" \
    -F "parse_mode=HTML" \
    2>&1)

# Check if the API call succeeded
if echo "$RESPONSE" | grep -q '"ok":true'; then
    echo "✅ Mindmap sent successfully to Telegram"
    # Extract message_id for reference
    MSG_ID=$(echo "$RESPONSE" | grep -o '"message_id":[0-9]*' | head -1 | cut -d: -f2)
    if [[ -n "$MSG_ID" ]]; then
        echo "Message ID: $MSG_ID"
    fi
else
    echo "❌ Failed to send mindmap to Telegram" >&2
    echo "Response: $RESPONSE" >&2

    # Parse common errors
    if echo "$RESPONSE" | grep -q "chat not found"; then
        echo "Hint: Check that chat_id ($CHAT_ID) is correct and the bot has access" >&2
    elif echo "$RESPONSE" | grep -q "bot was blocked"; then
        echo "Hint: The user has blocked the bot. They need to unblock and /start again" >&2
    elif echo "$RESPONSE" | grep -q "Unauthorized"; then
        echo "Hint: Bot token is invalid. Check TELEGRAM_BOT_TOKEN" >&2
    fi

    exit 1
fi


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